use chrono::offset::Utc;
use chrono::{DateTime, ParseError};
use std::collections::HashMap;
use std::num::ParseIntError;
use xmltree::Element;
#[derive(Debug, PartialEq)]
pub enum Error {
NotFoundAtPath { path: Vec<String> },
ExpectedNotEmpty { parent: String },
ExpectedElementWithType {
name: String,
expected_type: String,
given: Option<String>,
},
ParseIntError { name: String, inner: ParseIntError },
ParseDateTimeError { name: String, inner: ParseError },
}
pub trait BuildElement {
fn cloned(&self) -> Self;
fn node<S>(name: S) -> Self
where
S: Into<String>;
fn with_name<S>(self, name: S) -> Self
where
S: Into<String>;
fn with_text<S>(self, text: S) -> Self
where
S: Into<String>;
fn with_attr<KS, VS>(self, key: KS, value: VS) -> Self
where
KS: Into<String>,
VS: Into<String>;
fn with_child(self, child: Self) -> Self;
fn with_children<I>(self, children: I) -> Self
where
Self: Sized,
I: IntoIterator<Item = Self>;
fn with_children_from_iter<'r, I>(self, children: I) -> Self
where
Self: 'r + Sized,
I: Iterator<Item = &'r Self>;
fn to_string(&self) -> String;
fn descend(self, path: &[&str]) -> Result<Element, Error>;
fn descend_first(self) -> Result<Element, Error>;
fn get_at_path(&self, path: &[&str]) -> Result<Element, Error>;
fn as_long(&self) -> Result<i64, Error>;
fn as_int(&self) -> Result<i32, Error>;
fn as_boolean(&self) -> Result<bool, Error>;
fn as_string(&self) -> Result<String, Error>;
fn as_datetime(&self) -> Result<DateTime<Utc>, Error>;
}
impl BuildElement for Element {
fn cloned(&self) -> Self {
Element {
name: self.name.clone(),
attributes: self.attributes.clone(),
children: self.children.iter().map(|child| child.cloned()).collect(),
text: self.text.clone(),
namespace: self.namespace.clone(),
namespaces: self.namespaces.clone(),
prefix: self.prefix.clone(),
}
}
fn node<S>(name: S) -> Self
where
S: Into<String>,
{
Element {
name: name.into(),
attributes: HashMap::new(),
children: Vec::new(),
text: None,
namespace: None,
namespaces: None,
prefix: None,
}
}
fn with_name<S>(mut self, name: S) -> Self
where
S: Into<String>,
{
self.name = name.into();
self
}
fn with_text<S>(mut self, text: S) -> Self
where
S: Into<String>,
{
self.text = Some(text.into());
self
}
fn with_attr<KS, VS>(mut self, key: KS, value: VS) -> Self
where
KS: Into<String>,
VS: Into<String>,
{
self.attributes.insert(key.into(), value.into());
self
}
fn with_child(mut self, child: Self) -> Self {
self.children.push(child);
self
}
fn with_children<I>(mut self, children: I) -> Self
where
I: IntoIterator<Item = Self>,
{
self.children.extend(children);
self
}
fn with_children_from_iter<'r, I>(mut self, children: I) -> Self
where
I: Iterator<Item = &'r Self>,
{
for child in children {
self.children.push(child.cloned());
}
self
}
fn to_string(&self) -> String {
let mut xml = Vec::new();
self.write(&mut xml)
.unwrap_or_else(|e| println!("Unable to write xml: {:?}", e));
String::from_utf8_lossy(&xml).into_owned()
}
fn descend(self, path: &[&str]) -> Result<Element, Error> {
if path.is_empty() {
Ok(self)
} else {
for child in self.children {
if child.name == path[0] {
return match child.descend(&path[1..]) {
Ok(element) => Ok(element),
Err(Error::NotFoundAtPath {
path: mut error_path,
}) => {
error_path.insert(0, path[0].into());
Err(Error::NotFoundAtPath { path: error_path })
}
_ => unreachable!("descend should only return NotFoundAtPath error"),
};
}
}
Err(Error::NotFoundAtPath {
path: vec![path[0].into()],
})
}
}
fn descend_first(mut self) -> Result<Element, Error> {
if self.children.is_empty() {
Err(Error::ExpectedNotEmpty { parent: self.name })
} else {
Ok(self.children.remove(0))
}
}
fn get_at_path(&self, path: &[&str]) -> Result<Element, Error> {
if path.is_empty() {
Ok(self.cloned())
} else {
for child in &self.children {
if child.name == path[0] {
return match child.get_at_path(&path[1..]) {
Ok(element) => Ok(element),
Err(Error::NotFoundAtPath {
path: mut error_path,
}) => {
error_path.insert(0, path[0].into());
Err(Error::NotFoundAtPath { path: error_path })
}
_ => unreachable!("descend should only return NotFoundAtPath error"),
};
}
}
Err(Error::NotFoundAtPath {
path: vec![path[0].into()],
})
}
}
fn as_int(&self) -> Result<i32, Error> {
let text = try!(get_typed_string(self, "int"));
Ok(match text.parse() {
Ok(ref value) => *value,
Err(e) => {
return Err(Error::ParseIntError {
name: self.name.clone(),
inner: e,
});
}
})
}
fn as_long(&self) -> Result<i64, Error> {
let text = try!(get_typed_string(self, "long"));
Ok(match text.parse() {
Ok(ref value) => *value,
Err(e) => {
return Err(Error::ParseIntError {
name: self.name.clone(),
inner: e,
});
}
})
}
fn as_string(&self) -> Result<String, Error> {
get_typed_string(self, "string")
}
fn as_datetime(&self) -> Result<DateTime<Utc>, Error> {
let text = try!(get_typed_string(self, "dateTime"));
Ok(match text.parse::<DateTime<Utc>>() {
Ok(ref value) => *value,
Err(e) => {
return Err(Error::ParseDateTimeError {
name: self.name.clone(),
inner: e,
});
}
})
}
fn as_boolean(&self) -> Result<bool, Error> {
let text = try!(get_typed_string(self, "boolean"));
Ok(text == "true")
}
}
fn get_typed_string(element: &Element, value_type: &str) -> Result<String, Error> {
Ok(match (element.attributes.get("type"), &element.text) {
(Some(value), &Some(ref text)) if value.ends_with(value_type) => text.clone(),
(other_type, _) => {
return Err(Error::ExpectedElementWithType {
name: element.name.clone(),
expected_type: ["*:", value_type].concat(),
given: other_type.cloned(),
});
}
})
}