使用serde,是否可以反序列化为实现类型的结构?

时间:2019-04-05 13:40:42

标签: rust deserialization serde

我正在构建类似流氓的软件,我已经使数据加载器正常工作,并且ECS的一部分正在工作(从头开始构建)。数据存储在.yml文件中,用于描述游戏中的事物(在这种情况下为暴民)以及这些事物具有什么功能,例如:

---
orc:
  feature_packs:
    - physical
    - basic_identifiers_mob
  features:
    - component: char
      initial_value: T
goblin:
  feature_packs:
    - physical
    - basic_identifiers_mob
  features:
    - component: char
      initial_value: t

如您所见,这里描述了两个小怪,一个是地精,另一个是兽人,它们都具有两个功能包(一组功能),还具有char功能,用于描述它们的外观播放器。

initial_value字段可以是字符串,整数,浮点数,布尔值,范围等,具体取决于组件所需的内容,这将指示组件在执行以下操作时可能具有的值或可能的值组件是在实体构建/创建期间生成的。

问题是我不知道如何遍历这些功能时,根据组件的名称选择结构,例如,为Char功能选择"char"结构。

为了更好地描述我的意思,我用一个更好理解的语言Ruby编写了一个示例:

data_manager = function_that_loads_data('folder_path')

Entity_Manager.build(:mob, :orc, data_manager)

class Entity_Manager
  class << self
    attr_accessor :entities, :components
  end

  def self.build(entity_type, template_name, data_manager)
    template = data_manager[entity_type][template_name]
    entity_id = generate_unique_id
    entities[entity_id] = Entity.new(entity_id, components: template.components.keys)
    template.components.each do |component|
      components[component.name][entity_id] =
        Components.get(component.name).new(component.initial_value) # <= This part, how do I do the equivalent in rust, a function that will return or allow me to get or create a struct based on the value of a string variable 
    end
  end
end

我知道,现在只有Serde似乎能够读取文本数据并将其转换为数据,所以为此

我如何使用Serde(或更合适的非Serde使用解决方案)获取功能名称并检索正确的结构,都实现一个类型?

顺便说一句,我尝试不使用的一个解决方案是一个巨大的match语句。

我目前的作品回购是here

  • Data manager-加载和管理加载到游戏中的数据
  • Entity manager-管理实体及其组件(不支持atm的位键)
  • Entity Builder-使用数据管理器中的数据构建实体的位置(这是我目前停留的位置)
  • Components-简单组件列表

我要避免的是做这样的事情:

pub fn get(comp_name: &String) -> impl Component {
    match comp_name.as_ref() {
        "kind"      => Kind,
        "location"  => Location,
        "name"      => Name,
        "position"  => Position,
        "char"      => Char,
    }
}

因为它不是真正可维护的,尽管宏会带来很大帮助,但我对那些atm并不是很擅长,甚至无法正常工作,rust一直认为我在尝试初始化类型时只是想返回所有将实现Component

的几种可能类型之一

编辑:因为看起来我不够清楚:

  • 我不是要在游戏中加载游戏对象,而是要加载模板
  • 我正在使用这些模板来生成在游戏过程中将存在的实体
  • 我已经可以通过以下结构将所需的数据加载到游戏中:
pub enum InitialValue {
    Char(char),
    String(String),
    Int(i32),
    Float(f32),
    Bool(bool),
    Range(Range<i32>),
    Point((i32,i32))
}


impl InitialValue {

    pub fn unwrap_char(&self) -> &char {
        match &self {
            InitialValue::Char(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_string(&self) -> &String {
        match &self {
            InitialValue::String(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_int(&self) -> &i32 {
        match &self {
            InitialValue::Int(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_float(&self) -> &f32 {
        match &self {
            InitialValue::Float(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_bool(&self) -> &bool {
        match &self {
            InitialValue::Bool(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_range(&self) -> &Range<i32> {
        match &self {
            InitialValue::Range(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_point(&self) -> &(i32, i32) {
        match &self {
            InitialValue::Point(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct Component {
    #[serde(rename="component")]
    name: String,
    #[serde(default)]
    initial_value: Option<InitialValue>,
}

#[derive(Debug, Deserialize)]
pub struct Template {
    pub feature_packs: Vec<String>,
    pub features: Vec<Component>,
}
  • 如何将模板转换为实体实例?

  • 具体地说,如何为给定的Component.name查找组件 然后初始化它?还是我的方法是错误的,还有更好的方法 方式。

  • 如果我做错了,其他游戏如何加载数据,然后使用它来生成 游戏实体?

1 个答案:

答案 0 :(得分:1)

听起来像您想要标记的并集或求和类型; Rust知道这些为enumerations。 Serde甚至支持使用container internal tags。所以这是我的小实验:

#[macro_use] extern crate serde_derive;
extern crate serde_yaml;

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag="component")]
enum Feature {
    Char { initial_value : char },
    Weight { kgs : u32 }
}

fn main() {
    let v = vec![
        Feature::Char{initial_value:'x'},
        Feature::Weight{kgs:12}
    ];
    println!("{}", serde_yaml::to_string(&v).unwrap());
}

这将输出:

---
- component: Char
  initial_value: x
- component: Weight
  kgs: 12

可能的下一步是为这些变体制作专用的结构。