paramdex_rs/deserialize/
mod.rs

1use std::collections::HashMap;
2use std::num::{ParseFloatError, ParseIntError};
3use std::str::{FromStr, ParseBoolError};
4use roxmltree::Node;
5use thiserror::Error;
6use crate::{EditFlags, ParamDef, ParamdefEndian, ParamdefFormat, ParamField, ParamFieldDef};
7
8mod field_def_parse;
9
10pub use field_def_parse::DefParseError;
11
12/// Root element name of a ParamDef in the XML
13const PARAM_DEF_ROOT: &'static str = "PARAMDEF";
14
15impl FromStr for ParamDef {
16    type Err = ParamdefDeserializeError;
17
18    fn from_str(s: &str) -> Result<Self, Self::Err> {
19        deserialize_def(s)
20    }
21}
22
23/// Deserialize a ParamDef from a provided XML string
24pub fn deserialize_def<S: AsRef<str>>(input: S) -> Result<ParamDef, ParamdefDeserializeError> {
25    let input = input.as_ref();
26
27    let doc = roxmltree::Document::parse(input)?;
28
29    let root = doc.root_element();
30    if root.tag_name().name() != PARAM_DEF_ROOT {
31        return Err(ParamdefDeserializeError::MissingParamData("Invalid root element".into()));
32    }
33
34    let mut root_config: HashMap<String, String> = HashMap::new();
35
36    let mut fields: Option<Node> = None;
37
38    for child in root.children() {
39        match child.tag_name().name() {
40            "Fields" => {
41                fields.replace(child);
42            }
43            name => {
44                root_config.insert(name.into(), child.text().ok_or(ParamdefDeserializeError::XmlBlankElement(name.into()))?.into());
45            }
46        }
47    }
48
49    let fields_node = fields.ok_or(ParamdefDeserializeError::MissingParamData("Fields".into()))?;
50
51    let mut paramdef = ParamDef {
52        param_type: get_or_error(&root_config, "ParamType").cloned()?,
53        data_version: u32::from_str(get_or_error(&root_config, "DataVersion")?)?,
54        endian: ParamdefEndian::from_str(get_or_error(&root_config, "BigEndian")?)?,
55        string_format: ParamdefFormat::from_str(get_or_error(&root_config, "BigEndian")?)?,
56        format_version: u32::from_str(get_or_error(&root_config, "FormatVersion")?)?,
57        fields: Vec::new()
58    };
59
60    let fields = &mut paramdef.fields;
61
62    for node in fields_node.children() {
63        fields.push(parse_field_node(node)?);
64    }
65
66    Ok(paramdef)
67}
68
69/// Wrapper function to return value from a map or an error
70fn get_or_error<'a>(map: &'a HashMap<String, String>, key: &str) -> Result<&'a String, ParamdefDeserializeError> {
71    map.get(key).ok_or(ParamdefDeserializeError::MissingParamData(format!("{}", key)))
72}
73
74fn parse_field_node(field_node: Node) -> Result<ParamField, ParamdefDeserializeError> {
75    let attr = field_node.attribute("Def").ok_or(ParamdefDeserializeError::MissingParamData("Field Def".into()))?;
76
77    let mut field_config: HashMap<String, String> = HashMap::new();
78
79    for child in field_node.children() {
80        if let Some(text) = child.text() {
81            field_config.insert(child.tag_name().name().into(), text.into());
82        }
83    }
84
85
86    Ok(ParamField {
87
88        field_def: ParamFieldDef::from_str(attr)?,
89
90        display_name: field_config.get("DisplayName").cloned(),
91        enum_tdf: field_config.get("Enum").cloned(),
92        description: field_config.get("Description").cloned(),
93        printf_format: field_config.get("DisplayFormat").cloned(),
94
95        edit_flags: field_config.get("EditFlags").map(|a| EditFlags::from_str(a)).swap()?, // TODO
96
97        minimum: field_config.get("Minimum").map(|a| f64::from_str(a.as_str())).swap()?,
98        maximum: field_config.get("Maximum").map(|a| f64::from_str(a.as_str())).swap()?,
99        increment: field_config.get("Increment").map(|a| f64::from_str(a.as_str())).swap()?,
100        sort_id: field_config.get("SortID").map(|a| usize::from_str(a.as_str())).swap()?,
101    })
102
103}
104
105impl FromStr for EditFlags {
106    type Err = ParamdefDeserializeError;
107
108    fn from_str(text: &str) -> Result<Self, Self::Err> {
109        Ok(EditFlags {
110            wrap: text.contains("Wrap"),
111            lock: text.contains("Lock"),
112        })
113    }
114}
115
116impl FromStr for ParamFieldDef {
117    type Err = DefParseError;
118
119    fn from_str(s: &str) -> Result<Self, Self::Err> {
120        field_def_parse::parse_param_field_def(s)
121    }
122}
123
124trait OptionResultExt<V, E> {
125    fn swap(self) -> Result<Option<V>, E>;
126}
127
128impl<V, E> OptionResultExt<V, E> for Option<Result<V, E>> {
129    fn swap(self) -> Result<Option<V>, E> {
130        match self {
131            Some(Ok(v)) => Ok(Some(v)),
132            Some(Err(e)) => Err(e),
133            None => Ok(None)
134        }
135    }
136}
137
138impl FromStr for ParamdefEndian {
139    type Err = ParseBoolError;
140
141    fn from_str(s: &str) -> Result<Self, Self::Err> {
142          bool::from_str(s.to_lowercase().as_str()).map(|a| a.into())
143    }
144}
145
146impl From<bool> for ParamdefEndian {
147    fn from(a: bool) -> Self {
148        if a {
149            Self::Big
150        } else {
151            Self::Little
152        }
153    }
154}
155
156impl FromStr for ParamdefFormat {
157    type Err = ParseBoolError;
158
159    fn from_str(s: &str) -> Result<Self, Self::Err> {
160        bool::from_str(s).map(|a| a.into())
161    }
162}
163
164impl From<bool> for ParamdefFormat {
165    fn from(a: bool) -> Self {
166        if a {
167            Self::UTF16
168        } else {
169            Self::ShiftJIS
170        }
171    }
172}
173
174#[derive(Error, Debug)]
175pub enum ParamdefDeserializeError {
176    #[error("XML parsing failed: {0}")]
177    XmlParsing(#[from] roxmltree::Error),
178
179    #[error("XML blank element: {0}")]
180    XmlBlankElement(String),
181
182    #[error("Parsing number from XML: {0}")]
183    XmlParsingNumber(#[from] ParseIntError),
184
185    #[error("Parsing bool from XML: {0}")]
186    XmlParsingBool(#[from] ParseBoolError),
187
188    #[error("Parsing float from XML: {0}")]
189    XmlParsingFloat(#[from] ParseFloatError),
190
191    #[error("A required field in the XML was missing")]
192    MissingParamData(String),
193
194    #[error("Failed to parse field def string")]
195    ParsingDefString(#[from] DefParseError)
196}