tmx 0.2.2

TMX is a library for loading [Tiled](https://mapeditor.org) maps in Rust
Documentation
use quick_xml::{
    events::{attributes::Attributes, Event},
    Reader,
};
use serde_json::{Map, Value};
use std::io::BufRead;

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("failed to parse string")]
    ParseStringError(#[from] std::string::FromUtf8Error),
    #[error("failed to deserialize XML")]
    XmlError(#[from] quick_xml::Error),
    #[error("unexpected EOF")]
    UnexpectedEof,
    #[error("invalid array")]
    InvalidArray,
}

fn parse_tag<B: BufRead>(
    reader: &mut Reader<B>,
    buf: &mut Vec<u8>,
    root: bool,
) -> Result<Map<String, Value>, Error> {
    let mut children = Map::new();

    loop {
        let event = reader.read_event(buf);

        let mut start_tag =
            |name: &[u8], attributes: Attributes, map: Map<String, Value>| -> Result<(), Error> {
                let mut map = map;

                for attribute in attributes {
                    let attribute = attribute?;
                    map.insert(
                        String::from_utf8(attribute.key.to_vec())?,
                        Value::String(String::from_utf8(attribute.unescaped_value()?.to_vec())?),
                    );
                }

                let key = String::from_utf8(name.to_vec())?;

                match &mut children.get_mut(&key) {
                    None => {
                        children.insert(key, Value::Array(vec![Value::Object(map)]));
                    }
                    Some(value) => {
                        value
                            .as_array_mut()
                            .ok_or(Error::InvalidArray)?
                            .push(Value::Object(map));
                    }
                }

                Ok(())
            };

        match event {
            Ok(Event::Start(ref e)) => {
                let mut buf = vec![];
                start_tag(
                    e.name(),
                    e.attributes(),
                    parse_tag(reader, &mut buf, false)?,
                )?;
            }
            Ok(Event::End(ref _e)) => {
                break;
            }
            Ok(Event::Empty(ref e)) => {
                start_tag(e.name(), e.attributes(), Map::new())?;
            }
            Ok(Event::Text(ref e)) => {
                let string = e.unescape_and_decode(&reader)?;

                if string.trim().is_empty() {
                    continue;
                }

                children.insert("_".to_string(), Value::String(string));
            }
            Ok(Event::Comment(ref _e)) => {}
            Ok(Event::CData(ref _e)) => {}
            Ok(Event::Decl(ref _e)) => {}
            Ok(Event::PI(ref _e)) => {}
            Ok(Event::DocType(ref _e)) => {}
            Ok(Event::Eof) => {
                if root {
                    break;
                }

                return Err(Error::UnexpectedEof);
            }
            Err(e) => return Err(Error::XmlError(e)),
        }

        buf.clear();
    }

    Ok(children)
}

pub fn to_json(xml: &str) -> Result<Value, Error> {
    let mut buf = vec![];
    let mut reader = Reader::from_str(xml);

    Ok(Value::Object(parse_tag(&mut reader, &mut buf, true)?))
}