diff options
Diffstat (limited to 'src/lowlevel/property.rs')
-rw-r--r-- | src/lowlevel/property.rs | 278 |
1 files changed, 228 insertions, 50 deletions
diff --git a/src/lowlevel/property.rs b/src/lowlevel/property.rs index 26dcedd..bba693f 100644 --- a/src/lowlevel/property.rs +++ b/src/lowlevel/property.rs @@ -1,3 +1,4 @@ +use crate::{ensure_element, ensure_tag_name, get_attribute}; use anyhow::anyhow; use rgb::RGBA8; @@ -18,19 +19,14 @@ pub enum Property { Object { name: String, value: usize }, } +#[allow(dead_code)] impl Property { pub fn from_xml(node: roxmltree::Node) -> anyhow::Result<Property> { - if node.node_type() != roxmltree::NodeType::Element { - return Err(anyhow!("xml -> node is not an element")); - } + ensure_element!(node); + ensure_tag_name!(node, "property"); - let name = node - .attribute("name") - .ok_or(anyhow!("property name missing"))? - .to_owned(); - let property_type = node - .attribute("type") - .ok_or(anyhow!("property type missing"))?; + let name = get_attribute!(node, "name")?.to_owned(); + let property_type = get_attribute!(node, "type")?; // handle the case that 'string' value is stored in element content instead of value atribute if property_type == "string" { @@ -46,9 +42,7 @@ impl Property { } } - let raw_value = node - .attribute("value") - .ok_or(anyhow!("property value missing"))?; + let raw_value = get_attribute!(node, "value")?; match property_type { "string" => Ok(Property::String { @@ -106,6 +100,7 @@ impl Property { } } + #[allow(dead_code)] pub fn value_to_string(self) -> String { match self { Property::String { value, .. } => value, @@ -126,32 +121,124 @@ impl Property { } // Wraps any number of custom properties -#[derive(Debug)] +#[allow(dead_code)] +#[derive(Debug, PartialEq)] pub struct Properties { pub properties: Vec<Property>, } +impl Default for Properties { + fn default() -> Self { + Properties { properties: vec![] } + } +} + impl Properties { - fn from_xml(node: roxmltree::Node) -> anyhow::Result<Properties> { - Err(anyhow!("not implemented")) + pub fn from_xml(node: roxmltree::Node) -> anyhow::Result<Properties> { + let mut properties: Vec<Property> = Vec::new(); + for property_node in node.children() { + if property_node.has_tag_name("property") { + properties.push(Property::from_xml(property_node)?) + } + } + Ok(Properties { properties }) } } #[cfg(test)] -mod tests { +mod parse_property { use crate::lowlevel::property::Property; use rgb::RGBA8; #[test] - fn parse_color_property() { - // Find element by id - let doc = roxmltree::Document::parse( + fn string_inside_attribute() { + crate::parse_property_test!( + Property, + r##"<property name="enemyText" type="string" value="hello world"/>"##, + Property::String { + name: "enemyText".to_owned(), + value: "hello world".to_owned() + } + ); + } + + #[test] + fn string_inside_element() { + crate::parse_property_test!( + Property, + r##"<property name="enemyText" type="string">hello world</property>"##, + Property::String { + name: "enemyText".to_owned(), + value: "hello world".to_owned() + } + ); + } + + #[test] + fn int() { + crate::parse_property_test!( + Property, + r##"<property name="enemyText" type="int" value="-8"/>"##, + Property::Int { + name: "enemyText".to_owned(), + value: -8 + } + ); + crate::parse_property_test!( + Property, + r##"<property name="enemyText" type="int" value="3453"/>"##, + Property::Int { + name: "enemyText".to_owned(), + value: 3453 + } + ); + } + + #[test] + fn float() { + crate::parse_property_test!( + Property, + r##"<property name="ExperienceBoost" type="float" value="-8.8"/>"##, + Property::Float { + name: "ExperienceBoost".to_owned(), + value: -8.8 + } + ); + crate::parse_property_test!( + Property, + r##"<property name="ExperienceBoost" type="float" value="0.005"/>"##, + Property::Float { + name: "ExperienceBoost".to_owned(), + value: 0.005 + } + ); + } + + #[test] + fn bool() { + crate::parse_property_test!( + Property, + r##"<property name="isNight" type="bool" value="true"/>"##, + Property::Bool { + name: "isNight".to_owned(), + value: true + } + ); + crate::parse_property_test!( + Property, + r##"<property name="isNight" type="bool" value="false"/>"##, + Property::Bool { + name: "isNight".to_owned(), + value: false + } + ); + } + + #[test] + fn color() { + crate::parse_property_test!( + Property, r##"<property name="enemyTint" type="color" value="#ffa33636"/>"##, - ) - .unwrap(); - let elem = doc.root_element(); - assert_eq!( - Property::from_xml(elem).unwrap(), Property::Color { name: "enemyTint".to_owned(), value: RGBA8 { @@ -161,41 +248,132 @@ mod tests { a: 54 } } + ) + } + + #[test] + fn file() { + crate::parse_property_test!( + Property, + r##"<property name="music" type="file" value="../music/cave.ogg"/>"##, + Property::File { + name: "music".to_owned(), + value: "../music/cave.ogg".to_owned() + } ); } #[test] - fn parse_string_property_inside_attribute() { - // Find element by id - let doc = roxmltree::Document::parse( - r##"<property name="enemyText" type="string" value="hello world"/>"##, - ) - .unwrap(); - let elem = doc.root_element(); - assert_eq!( - Property::from_xml(elem).unwrap(), - Property::String { - name: "enemyText".to_owned(), - value: "hello world".to_owned() + fn object() { + crate::parse_property_test!( + Property, + r##"<property name="music" type="object" value="2583"/>"##, + Property::Object { + name: "music".to_owned(), + value: 2583 } ); } +} + +#[cfg(test)] +mod parse_properties { + use crate::lowlevel::property::Properties; + use crate::lowlevel::property::Property; + use rgb::RGBA8; #[test] - fn parse_string_property_inside_element() { - // Find element by id - let doc = roxmltree::Document::parse( - r##"<property name="enemyText" type="string">hello world</property>"##, - ) - .unwrap(); - let elem = doc.root_element(); - println!("{:?}", elem.first_child()); - assert_eq!( - Property::from_xml(elem).unwrap(), - Property::String { - name: "enemyText".to_owned(), - value: "hello world".to_owned() + fn one_property() { + crate::parse_property_test!( + Properties, + r##"<properties> + <property name="enemyTint" type="color" value="#ffa33636"/> + </properties>"##, + Properties { + properties: vec![Property::Color { + name: "enemyTint".to_owned(), + value: RGBA8 { + r: 255, + g: 163, + b: 54, + a: 54 + } + }] + } + ); + } + + #[test] + fn two_properties() { + crate::parse_property_test!( + Properties, + r##"<properties> + <property name="enemyTint" type="color" value="#ffa33636"/> + <property name="enemyDropChanceModifier" type="float" value="0.005"/> + </properties>"##, + Properties { + properties: vec![ + Property::Color { + name: "enemyTint".to_owned(), + value: RGBA8 { + r: 255, + g: 163, + b: 54, + a: 54 + } + }, + Property::Float { + name: "enemyDropChanceModifier".to_owned(), + value: 0.005 + } + ] + } + ); + } + + #[test] + fn two_properties_with_comments() { + crate::parse_property_test!( + Properties, + r##"<properties> + <!-- what color the enemy should have --> + <property name="enemyTint" type="color" value="#ffa33636"/> + <!-- a nother comment --> + <property name="enemyDropChanceModifier" type="float" value="0.005"/> + </properties>"##, + Properties { + properties: vec![ + Property::Color { + name: "enemyTint".to_owned(), + value: RGBA8 { + r: 255, + g: 163, + b: 54, + a: 54 + } + }, + Property::Float { + name: "enemyDropChanceModifier".to_owned(), + value: 0.005 + } + ] } ); } + + #[test] + fn zero_properties() { + crate::parse_property_test!( + Properties, + r##"<properties></properties>"##, + Properties { properties: vec![] } + ); + } + + #[test] + fn default_value() { + let empty_properties: Properties = Default::default(); + + assert_eq!(empty_properties.properties.len(), 0) + } } |