如何制作可以反序列化为几种类型之一的config.rs字段?

时间:2018-09-29 11:33:33

标签: rust serde

我正在使用config作为将外部数据加载到程序中的一种方法,该程序在后台使用serde进行反序列化,但是我希望特定字段可以是几种类型之一。 因为我对Rust还是很陌生,所以发现的documentation并没有多大意义。

如何使initial_value成为几种类型之一:

  • String
  • i32
  • bool
  • [i32,i32]

我知道我可以使用属性deserialize_with定向到自定义解码器,但除此之外,我有点茫然。

这是我的代码:

use std::collections::HashMap;
use std::fs;

use config_rs::{Config, ConfigError, File};

#[derive(Debug, Deserialize)]
enum InitialValues {
    String,
    i32,
    bool,
}

#[derive(Debug, Deserialize)]
struct Component {
    component: String,
    initial_value: InitialValues,
}

#[derive(Debug, Deserialize)]
struct Template {
    feature_packs: Vec<String>,
    components: Vec<Component>,
}

type Name = String;
type Type = String;

#[derive(Debug, Deserialize)]
pub struct Templates {
    templates: HashMap<Name, Template>,
}

impl Templates {
    pub fn new(file: &str) -> Result<Self, ConfigError> {
        let mut templates = Config::new();
        templates.merge(File::with_name(file)).unwrap();
        templates.try_into()
    }
}

#[derive(Debug)]
pub struct Manager {
    pub templates: HashMap<Type, Templates>,
}

impl Manager {
    pub fn new(dir: &str) -> Self {
        let mut manager = Self {
            templates: HashMap::new(),
        };
        'paths: for raw_path in fs::read_dir(dir).unwrap() {
            let path = raw_path.unwrap().path();
            let file_path = path.clone();
            let type_name = path.clone();
            let templates = match Templates::new(type_name.to_str().unwrap()) {
                Ok(templates) => templates,
                Err(message) => {
                    println!("TemplateManager: file({:?}), {}", &file_path, message);
                    continue 'paths;
                }
            };
            manager.templates.insert(
                file_path.file_stem().unwrap().to_str().unwrap().to_owned(),
                templates,
            );
        }
        manager
    }
}

示例配置文件:

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

2 个答案:

答案 0 :(得分:3)

我将反序列化您提供的示例配置,如下所示。


fetch('http://uinames.com/api/?amount=25&region=denmark&gender=female')
  .then(res => res.json()) // returns response data as JSON object
  .then(jsonData => {
    const destinationTable = document.querySelector('table'); // select table to which you want to append the data
    jsonData.forEach(record => {
      const tr = document.createElement('tr'); // create a table row
      const tdGender = document.createElement('td'); // create a table cell
      tdGender.innerText = record.gender; // insert gender to the created table cell
      tr.appendChild(tdGender); // append table cell to the created row
      destinationTable.appendChild(tr); // append created row to the table
    })
  });

答案 1 :(得分:0)

InitialValues enum更改为:

#[derive(Debug, Deserialize)]
#[serde(
    rename_all = "lowercase",
    untagged
)]
pub enum InitialValue {
    Char(char),
    String(String),
    Int(i32),
    Float(f32),
    Bool(bool),
    Range(Range<i32>)
}

表示以下各项现在有效:

---
example:
  feature_packs:
    - example
    - example
  components:
    - component: char
      initial_value: T
    - component: string
      initial_value: 'asdasdasd'
    - component: int
      initial_value: 2
    - component: float
      initial_value: 3.2
    - component: bool
      initial_value: true
    - component: range
      initial_value:
        start: 0
        end: 9

比我想要的还要冗长,但至少它是可行的。


编辑:感谢@dtolnay的回答,我得以改进此问题,现在它完全按照我的原意读取数据了!

上面的代码已被修改以反映这一点。


此外,通过#[serde(flatten)]Templates就像这样:

#[derive(Debug, Deserialize)]
pub struct Templates {
    #[serde(flatten)]
    templates: HashMap<Name,Template>,
}

我能够不必在文件开头添加templates: