nv-xml 0.1.3

A very light weight xml library
Documentation
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}
	}

}