summaryrefslogtreecommitdiff
path: root/src/lowlevel/property.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lowlevel/property.rs')
-rw-r--r--src/lowlevel/property.rs201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/lowlevel/property.rs b/src/lowlevel/property.rs
new file mode 100644
index 0000000..26dcedd
--- /dev/null
+++ b/src/lowlevel/property.rs
@@ -0,0 +1,201 @@
+use anyhow::anyhow;
+use rgb::RGBA8;
+
+// https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#property
+
+// name: The name of the property.
+// type: The type of the property. Can be string (default), int, float, bool, color, file or object (since 0.16, with color and file added in 0.17, and object added in 1.4).
+// value: The value of the property. (default string is “”, default number is 0, default boolean is “false”, default color is #00000000, default file is “.” (the current file’s parent directory))
+
+#[derive(Debug, PartialEq)]
+pub enum Property {
+ String { name: String, value: String },
+ Int { name: String, value: isize },
+ Float { name: String, value: f64 },
+ Bool { name: String, value: bool },
+ Color { name: String, value: RGBA8 },
+ File { name: String, value: String },
+ Object { name: String, value: usize },
+}
+
+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"));
+ }
+
+ 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"))?;
+
+ // handle the case that 'string' value is stored in element content instead of value atribute
+ if property_type == "string" {
+ if let Some(child) = node.first_child() {
+ if let Some(text) = child.text() {
+ return Ok(Property::String {
+ name,
+ value: text.to_owned(),
+ });
+ } else {
+ return Err(anyhow!("property element content is not of NodeType::Text"));
+ }
+ }
+ }
+
+ let raw_value = node
+ .attribute("value")
+ .ok_or(anyhow!("property value missing"))?;
+
+ match property_type {
+ "string" => Ok(Property::String {
+ name,
+ value: raw_value.to_owned(),
+ }),
+ "int" => Ok(Property::Int {
+ name,
+ value: raw_value.parse()?,
+ }),
+ "float" => Ok(Property::Float {
+ name,
+ value: raw_value.parse()?,
+ }),
+ "bool" => {
+ let value = match raw_value {
+ "true" => Ok(true),
+ "false" => Ok(false),
+ _ => Err(anyhow!(
+ "boolean property has unexpected value: {}",
+ raw_value
+ )),
+ };
+ Ok(Property::Bool {
+ name,
+ value: value?,
+ })
+ }
+ "color" => {
+ let mut chars = raw_value.chars();
+ chars.next(); // remove the `#`
+ if let Some(color) = read_color::rgba(&mut chars) {
+ Ok(Property::Color {
+ name,
+ value: RGBA8 {
+ r: color[0],
+ g: color[1],
+ b: color[2],
+ a: color[3],
+ },
+ })
+ } else {
+ Err(anyhow!("could not parse color"))
+ }
+ }
+ "file" => Ok(Property::File {
+ name,
+ value: raw_value.to_owned(),
+ }),
+ "object" => Ok(Property::Object {
+ name,
+ value: raw_value.parse()?,
+ }),
+ _ => Err(anyhow!("unknown type {}", property_type)),
+ }
+ }
+
+ pub fn value_to_string(self) -> String {
+ match self {
+ Property::String { value, .. } => value,
+ Property::Int { value, .. } => format!("{}", value),
+ Property::Float { value, .. } => format!("{}", value),
+ Property::Bool { value, .. } => match value {
+ true => "true",
+ false => "false",
+ }
+ .to_owned(),
+ Property::Color { value, .. } => {
+ format!("#{:x}{:x}{:x}{:x}", value.r, value.g, value.b, value.a)
+ }
+ Property::File { value, .. } => value,
+ Property::Object { value, .. } => format!("{}", value),
+ }
+ }
+}
+
+// Wraps any number of custom properties
+#[derive(Debug)]
+pub struct Properties {
+ pub properties: Vec<Property>,
+}
+
+impl Properties {
+ fn from_xml(node: roxmltree::Node) -> anyhow::Result<Properties> {
+ Err(anyhow!("not implemented"))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::lowlevel::property::Property;
+ use rgb::RGBA8;
+
+ #[test]
+ fn parse_color_property() {
+ // Find element by id
+ let doc = roxmltree::Document::parse(
+ 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 {
+ r: 255,
+ g: 163,
+ b: 54,
+ a: 54
+ }
+ }
+ );
+ }
+
+ #[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()
+ }
+ );
+ }
+
+ #[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()
+ }
+ );
+ }
+}