use XmlParser;
use XmlAttribute;
use xml::reader::{XmlEvent};
use xml::namespace::{Namespace};
use xml::name::{OwnedName};
use xml::attribute::{OwnedAttribute};
use std::string::{ToString};
#[derive(Debug, Clone)]
pub struct XmlElement
{
_event : XmlEvent,
_children : Vec<XmlElement>,
_data: Option<XmlEvent>,
}
impl XmlElement
{
pub fn new(event : XmlEvent) -> XmlElement
{
Self::new_with_children(event, Vec::new())
}
pub fn new_with_children(event : XmlEvent, children: Vec<XmlElement>) -> XmlElement
{
Self::custom(event, children, None)
}
pub fn custom(event : XmlEvent, children: Vec<XmlElement>, data: Option<XmlEvent>) -> XmlElement
{
XmlElement{_event: event, _children:children, _data: data}
}
pub fn parse(data: &str) -> Option<XmlElement>
{
XmlParser::parse(data).ok()
}
pub fn name(&self) -> &str
{
match self._event
{
XmlEvent::StartElement{ref name, ..} => &name.local_name,
_ => panic!("somethings gone wrong")
}
}
pub fn mut_change_name_to(&mut self, new_name: &str) -> ()
{
match self._event
{
XmlEvent::StartElement{ref mut name, ..} => name.local_name = new_name.to_owned(),
_ => panic!("somethings gone wrong")
}
}
pub fn change_name_to(&self, new_name: &str) -> XmlElement
{
let name = self.inner_name().clone();
let owned_name = OwnedName{local_name: new_name.to_owned(),
namespace: name.namespace,
prefix: name.prefix};
let start_element = XmlEvent::StartElement{name:owned_name,
attributes: self.inner_attributes().to_vec(),
namespace: self.inner_namespace().clone()};
Self::new_with_children(start_element, self.children().clone().to_vec())
}
pub fn mut_change_data(&mut self, new_data: &str) -> ()
{
self._data = Some(XmlEvent::Characters(new_data.to_owned()))
}
pub fn data(&self) -> Option<&str>
{
match self._data
{
None => None,
Some(ref e) => Some(Self::extract_data(e))
}
}
fn extract_data(event : &XmlEvent) -> &str
{
match event
{
&XmlEvent::Characters(ref c) => c,
_ => panic!("somethings gone wrong")
}
}
pub fn has_attributes(&self) -> bool
{
!self.attributes().is_empty()
}
pub fn attributes(&self) -> Vec<XmlAttribute>
{
self.inner_attributes().iter().map(|a| XmlAttribute::new(&a)).collect()
}
pub fn attribute_with_name(&self, name : &str) -> Option<XmlAttribute>
{
self.attributes()
.into_iter()
.filter(|a| a.name() == name)
.next()
}
pub fn attribute_value_of(&self, name: &str) -> Option<&str>
{
self.attribute_with_name(name).map(|a| a.value())
}
pub fn children_with_name(&self, name : &str) -> Vec<&XmlElement>
{
self._children.iter().filter(|c| c.name() == name).collect()
}
pub fn descendents_with_name(&self, name : &str) -> Vec<&XmlElement>
{
self._children.iter()
.flat_map(|c| Self::descendents_from_child(c, name))
.collect()
}
fn descendents_from_child<'a>(child: &'a XmlElement, name: &str) -> Vec<&'a XmlElement>
{
let mut elements = child.descendents_with_name(name);
if child.name() == name
{elements.push(child)}
elements
}
pub fn children(&self) -> &Vec<XmlElement>
{
&self._children
}
pub fn mut_children(&mut self) -> &mut Vec<XmlElement>
{
&mut self._children
}
pub fn has_children(&self) -> bool
{
!self.children().is_empty()
}
fn inner_attributes(&self) -> &Vec<OwnedAttribute>
{
match self._event
{
XmlEvent::StartElement{ref attributes, ..} => attributes,
_ => panic!("somethings gone wrong")
}
}
fn inner_namespace(&self) -> &Namespace
{
match self._event
{
XmlEvent::StartElement{ref namespace, ..} => namespace,
_ => panic!("somethings gone wrong")
}
}
fn inner_name(&self) -> &OwnedName
{
match self._event
{
XmlEvent::StartElement{ref name, ..} => name,
_ => panic!("somethings gone wrong")
}
}
}
impl ToString for XmlElement
{
fn to_string(&self) -> String
{
let mut start_element = format!("<{}", self.name());
if self.has_attributes()
{
let attributes = self.attributes().iter().map(|a| a.to_string()).collect::<Vec<String>>().join(" ");
start_element = format!("{} {}", start_element, attributes);
}
if !self.has_children()
{
return format!("{}/>", start_element);
}
start_element.push('>');
let end = format!("</{}>", self.name());
let children = self.children().iter().map(|c| "\t".to_owned() + &c.to_string()).collect::<Vec<String>>().join("\n");
[start_element, children, end].join("\n")
}
}
#[cfg(test)]
mod xml_element_tests
{
use super::{XmlElement, XmlAttribute};
use xml::reader::{XmlEvent};
use xml::namespace::{Namespace};
use xml::name::{OwnedName};
use xml::attribute::{OwnedAttribute};
#[test]
fn can_create_an_element() -> ()
{
let name = OwnedName{local_name: "a name".to_owned(), namespace: None, prefix: None};
let start_element = XmlEvent::StartElement{name:name, attributes:Vec::new(), namespace: Namespace::empty()};
let element = XmlElement::new(start_element);
assert_eq!(element.name(), "a name");
}
#[test]
fn can_get_attributes_by_name() -> ()
{
let att = OwnedAttribute{name:owned_name_from("name"), value: "a value".to_owned()};
let start_element = XmlEvent::StartElement{name:owned_name_from("name"), attributes:vec![att], namespace: Namespace::empty()};
let element = XmlElement::new(start_element);
assert_eq!(element.attribute_with_name("name").map(|a| a.value().to_owned()).unwrap(), "a value");
}
#[test]
fn can_get_attributes_value_from_key() -> ()
{
let att = OwnedAttribute{name:owned_name_from("name"), value: "a value".to_owned()};
let start_element = XmlEvent::StartElement{name:owned_name_from("name"), attributes:vec![att], namespace: Namespace::empty()};
let element = XmlElement::new(start_element);
assert_eq!(element.attribute_value_of("name").unwrap(), "a value");
}
#[test]
fn attributes_have_names() -> ()
{
let att = OwnedAttribute{name:owned_name_from("name"), value: "a value".to_owned()};
let element = XmlAttribute::new(&att);
assert_eq!(element.name(), "name");
}
#[test]
fn attributes_have_values() -> ()
{
let att = OwnedAttribute{name:owned_name_from("name"), value: "a value".to_owned()};
let element = XmlAttribute::new(&att);
assert_eq!(element.value(), "a value");
}
#[test]
fn parse_can_fail() -> ()
{
let element = XmlElement::parse("<bad_element>");
assert_eq!(element.is_none(), true);
}
#[test]
fn to_string_no_children_test()
{
let data = "<data name=\"x\"></data>";
let element = XmlElement::parse(data);
assert_eq!(element.unwrap().to_string(), "<data name=\"x\"/>")
}
#[test]
fn to_string_children_test()
{
let data = "<data name=\"x\">\n\t<something/>\n</data>";
let element = XmlElement::parse(data);
assert_eq!(element.unwrap().to_string(), data)
}
#[test]
fn to_string_children_with_attributes_test()
{
let data = "<data name=\"x\">\n\t<something name=\"x\"/>\n</data>";
let element = XmlElement::parse(data);
assert_eq!(element.unwrap().to_string(), data)
}
#[test]
fn change_name_test()
{
let data = "<data name=\"x\">\n\t<something name=\"x\"/>\n</data>";
assert_eq!(XmlElement::parse(data).unwrap().change_name_to("y").name(), "y")
}
#[test]
fn mutating_name_change_test()
{
let data = "<data name=\"x\">\n\t<something name=\"x\"/>\n</data>";
let mut element = XmlElement::parse(data).unwrap();
assert_eq!(element.name(), "data");
element.mut_change_name_to("y");
assert_eq!(element.name(), "y");
}
fn owned_name_from(name: &str) -> OwnedName
{
OwnedName{local_name: name.to_owned(), namespace: None, prefix: None}
}
}