#![crate_name = "xml"]
#![crate_type = "lib"]
#![forbid(non_camel_case_types)]
#![warn(missing_docs)]
#![cfg_attr(feature = "bench", feature(test))]
pub use crate::element::ChildElements;
pub use crate::element::Element;
pub use crate::element_builder::BuilderError;
pub use crate::element_builder::ElementBuilder;
pub use crate::parser::Event;
pub use crate::parser::Parser;
pub use crate::parser::ParserError;
use std::char;
use std::collections::HashMap;
use std::fmt;
mod element;
mod element_builder;
mod parser;
#[inline]
pub fn escape(input: &str) -> String {
    let mut result = String::with_capacity(input.len());
    for c in input.chars() {
        match c {
            '&' => result.push_str("&"),
            '<' => result.push_str("<"),
            '>' => result.push_str(">"),
            '\'' => result.push_str("'"),
            '"' => result.push_str("""),
            o => result.push(o),
        }
    }
    result
}
#[inline]
pub fn unescape(input: &str) -> Result<String, String> {
    let mut result = String::with_capacity(input.len());
    let mut it = input.split('&');
    
    if let Some(sub) = it.next() {
        result.push_str(sub);
    }
    for sub in it {
        match sub.find(';') {
            Some(idx) => {
                let ent = &sub[..idx];
                match ent {
                    "quot" => result.push('"'),
                    "apos" => result.push('\''),
                    "gt" => result.push('>'),
                    "lt" => result.push('<'),
                    "amp" => result.push('&'),
                    ent => {
                        let val = if ent.starts_with("#x") {
                            u32::from_str_radix(&ent[2..], 16).ok()
                        } else if ent.starts_with('#') {
                            u32::from_str_radix(&ent[1..], 10).ok()
                        } else {
                            None
                        };
                        match val.and_then(char::from_u32) {
                            Some(c) => result.push(c),
                            None => return Err(format!("&{};", ent)),
                        }
                    }
                }
                result.push_str(&sub[idx + 1..]);
            }
            None => return Err("&".to_owned() + sub),
        }
    }
    Ok(result)
}
#[derive(Clone, PartialEq, Debug)]
pub enum Xml {
    
    ElementNode(Element),
    
    CharacterNode(String),
    
    CDATANode(String),
    
    CommentNode(String),
    
    PINode(String),
}
#[derive(PartialEq, Eq, Debug)]
pub struct StartTag {
    
    pub name: String,
    
    pub ns: Option<String>,
    
    pub prefix: Option<String>,
    
    pub attributes: HashMap<(String, Option<String>), String>,
}
#[derive(PartialEq, Eq, Debug)]
pub struct EndTag {
    
    pub name: String,
    
    pub ns: Option<String>,
    
    pub prefix: Option<String>,
}
impl fmt::Display for Xml {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Xml::ElementNode(ref elem) => elem.fmt(f),
            Xml::CharacterNode(ref data) => write!(f, "{}", escape(&data)),
            Xml::CDATANode(ref data) => write!(f, "<![CDATA[{}]]>", &data),
            Xml::CommentNode(ref data) => write!(f, "<!--{}-->", &data),
            Xml::PINode(ref data) => write!(f, "<?{}?>", &data),
        }
    }
}
#[cfg(test)]
mod lib_tests {
    use super::{escape, unescape, Element, Xml};
    #[test]
    fn test_escape() {
        let esc = escape("&<>'\"");
        assert_eq!(esc, "&<>'"");
    }
    #[test]
    fn test_unescape() {
        let unesc = unescape("&lt;<>'"“”&"");
        assert_eq!(
            unesc.as_ref().map(|x| &x[..]),
            Ok("<<>'\"\u{201c}\u{201d}&\""),
        );
    }
    #[test]
    fn test_unescape_invalid() {
        let unesc = unescape("& ");
        assert_eq!(unesc.as_ref().map_err(|x| &x[..]), Err(" "));
    }
    #[test]
    fn test_show_element() {
        let elem = Element::new("a".to_owned(), None, vec![]);
        assert_eq!(format!("{}", elem), "<a/>");
        let elem = Element::new(
            "a".to_owned(),
            None,
            vec![("href".to_owned(), None, "http://rust-lang.org".to_owned())],
        );
        assert_eq!(format!("{}", elem), "<a href='http://rust-lang.org'/>");
        let mut elem = Element::new("a".to_owned(), None, vec![]);
        elem.tag(Element::new("b".to_owned(), None, vec![]));
        assert_eq!(format!("{}", elem), "<a><b/></a>");
        let mut elem = Element::new(
            "a".to_owned(),
            None,
            vec![("href".to_owned(), None, "http://rust-lang.org".to_owned())],
        );
        elem.tag(Element::new("b".to_owned(), None, vec![]));
        assert_eq!(
            format!("{}", elem),
            "<a href='http://rust-lang.org'><b/></a>",
        );
    }
    #[test]
    fn test_show_element_xmlns() {
        let elem: Element = "<a xmlns='urn:test'/>".parse().unwrap();
        assert_eq!(format!("{}", elem), "<a xmlns='urn:test'/>");
        let elem: Element = "<a xmlns='urn:test'><b xmlns='urn:toast'/></a>"
            .parse()
            .unwrap();
        assert_eq!(
            format!("{}", elem),
            "<a xmlns='urn:test'><b xmlns='urn:toast'/></a>",
        );
        let elem = Element::new(
            "a".to_owned(),
            Some("urn:test".to_owned()),
            vec![("href".to_owned(), None, "http://rust-lang.org".to_owned())],
        );
        assert_eq!(
            format!("{}", elem),
            "<a xmlns='urn:test' href='http://rust-lang.org'/>",
        );
    }
    #[test]
    fn test_show_characters() {
        let chars = Xml::CharacterNode("some text".to_owned());
        assert_eq!(format!("{}", chars), "some text");
    }
    #[test]
    fn test_show_cdata() {
        let chars = Xml::CDATANode("some text".to_owned());
        assert_eq!(format!("{}", chars), "<![CDATA[some text]]>");
    }
    #[test]
    fn test_show_comment() {
        let chars = Xml::CommentNode("some text".to_owned());
        assert_eq!(format!("{}", chars), "<!--some text-->");
    }
    #[test]
    fn test_show_pi() {
        let chars = Xml::PINode("xml version='1.0'".to_owned());
        assert_eq!(format!("{}", chars), "<?xml version='1.0'?>");
    }
    #[test]
    fn test_content_str() {
        let mut elem = Element::new("a".to_owned(), None, vec![]);
        elem.pi("processing information".to_owned())
            .cdata("<hello/>".to_owned())
            .tag_stay(Element::new("b".to_owned(), None, vec![]))
            .text("World".to_owned())
            .comment("Nothing to see".to_owned());
        assert_eq!(elem.content_str(), "<hello/>World");
    }
}
#[cfg(test)]
#[cfg(feature = "bench")]
mod lib_bench {
    extern crate test;
    use self::test::Bencher;
    use super::{escape, unescape};
    use std::iter::repeat;
    #[bench]
    fn bench_escape(bh: &mut Bencher) {
        let input: String = repeat("&<>'\"").take(100).collect();
        bh.iter(|| escape(&input));
        bh.bytes = input.len() as u64;
    }
    #[bench]
    fn bench_unescape(bh: &mut Bencher) {
        let input: String = repeat("&<>'"").take(50).collect();
        bh.iter(|| unescape(&input));
        bh.bytes = input.len() as u64;
    }
}