Newer
Older
use std::{collections::HashMap, str::FromStr};
use xml::{attribute::OwnedAttribute, reader::XmlEvent};
error::TiledError,
util::{get_attrs, parse_tag, XmlEventResult},
fn from_str(s: &str) -> Result<Color, Self::Err> {
match s.len() {
6 => {
let r = u8::from_str_radix(&s[0..2], 16);
let g = u8::from_str_radix(&s[2..4], 16);
let b = u8::from_str_radix(&s[4..6], 16);
match (r, g, b) {
(Ok(red), Ok(green), Ok(blue)) => Ok(Color {
alpha: 0xFF,
red,
green,
blue,
}),
_ => Err(()),
}
}
8 => {
let a = u8::from_str_radix(&s[0..2], 16);
let r = u8::from_str_radix(&s[2..4], 16);
let g = u8::from_str_radix(&s[4..6], 16);
let b = u8::from_str_radix(&s[6..8], 16);
match (a, r, g, b) {
(Ok(alpha), Ok(red), Ok(green), Ok(blue)) => Ok(Color {
alpha,
red,
green,
blue,
}),
_ => Err(()),
}
}
_ => Err(()),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum PropertyValue {
BoolValue(bool),
FloatValue(f32),
IntValue(i32),
ColorValue(u32),
StringValue(String),
/// Holds the path relative to the map or tileset
FileValue(String),
/// Holds the id of a referenced object, or 0 if unset
ObjectValue(u32),
}
impl PropertyValue {
fn new(property_type: String, value: String) -> Result<PropertyValue, TiledError> {
// Check the property type against the value.
match property_type.as_str() {
"bool" => match value.parse() {
Ok(val) => Ok(PropertyValue::BoolValue(val)),
Err(err) => Err(TiledError::InvalidPropertyValue {
description: err.to_string(),
}),
},
"float" => match value.parse() {
Ok(val) => Ok(PropertyValue::FloatValue(val)),
Err(err) => Err(TiledError::InvalidPropertyValue {
description: err.to_string(),
}),
},
"int" => match value.parse() {
Ok(val) => Ok(PropertyValue::IntValue(val)),
Err(err) => Err(TiledError::InvalidPropertyValue {
description: err.to_string(),
}),
},
"color" if value.len() > 1 => match u32::from_str_radix(&value[1..], 16) {
Ok(color) => Ok(PropertyValue::ColorValue(color)),
Err(err) => Err(TiledError::InvalidPropertyValue {
description: err.to_string(),
}),
"object" => match value.parse() {
Ok(val) => Ok(PropertyValue::ObjectValue(val)),
Err(err) => Err(TiledError::InvalidPropertyValue {
description: err.to_string(),
}),
_ => Err(TiledError::UnknownPropertyType {
name: property_type,
}),
}
}
}
pub type Properties = HashMap<String, PropertyValue>;
pub(crate) fn parse_properties(
parser: &mut impl Iterator<Item = XmlEventResult>,
) -> Result<Properties, TiledError> {
let mut p = HashMap::new();
parse_tag!(parser, "properties", {
"property" => |attrs:Vec<OwnedAttribute>| {
let ((t, v_attr), k) = get_attrs!(
attrs,
optionals: [
("type", property_type, |v| Some(v)),
],
required: [
("name", key, |v| Some(v)),
],
TiledError::MalformedAttributes("property must have a name and a value".to_string())
);
let t = t.unwrap_or("string".into());
Some(val) => val,
None => {
// if the "value" attribute was missing, might be a multiline string
match parser.next() {
Some(Ok(XmlEvent::Characters(s))) => Ok(s),
Some(Err(err)) => Err(TiledError::XmlDecodingError(err)),
None => unreachable!(), // EndDocument or error must come first
_ => Err(TiledError::MalformedAttributes(format!("property '{}' is missing a value", k))),
}?
}
};
p.insert(k, PropertyValue::new(t, v)?);
Ok(())
},
});
Ok(p)
}