use std::iter::Peekable;
use std::num::ParseIntError;
use std::collections::HashMap;
use xml::reader::*;
use xml::reader::events::*;
#[derive(Debug)]
pub struct XmlParseError(pub String);
impl XmlParseError {
pub fn new(msg: &str) -> XmlParseError {
XmlParseError(msg.to_string())
}
}
pub type XmlStack<'a> = Peekable<Events<'a, &'a[u8]>>;
pub trait Peek {
fn peek(&mut self) -> Option<&XmlEvent>;
}
pub trait Next {
fn next(&mut self) -> Option<XmlEvent>;
}
pub struct XmlResponse<'b> {
xml_stack: Peekable<Events<'b, &'b [u8]>> }
impl <'b>XmlResponse<'b> {
pub fn new(stack: Peekable<Events<'b, &'b [u8]>>) -> XmlResponse {
XmlResponse {
xml_stack: stack,
}
}
}
impl <'b>Peek for XmlResponse<'b> {
fn peek(&mut self) -> Option<&XmlEvent> {
loop {
match self.xml_stack.peek() {
Some(&XmlEvent::Whitespace(_)) => { },
_ => break
}
self.xml_stack.next();
}
self.xml_stack.peek()
}
}
impl <'b> Next for XmlResponse<'b> {
fn next(&mut self) -> Option<XmlEvent> {
let mut maybe_event;
loop {
maybe_event = self.xml_stack.next();
match maybe_event {
Some(XmlEvent::Whitespace(_)) => {},
_ => break
}
}
maybe_event
}
}
impl From<ParseIntError> for XmlParseError{
fn from(_e:ParseIntError) -> XmlParseError { XmlParseError::new("ParseIntError") }
}
pub fn optional_string_field<T: Peek + Next>(field_name: &str, stack: &mut T) -> Result<Option<String>, XmlParseError> {
if try!(peek_at_name(stack)) == field_name {
let val = try!(string_field(field_name, stack));
Ok(Some(val))
} else {
Ok(None)
}
}
pub fn string_field<T: Peek + Next>(name: &str, stack: &mut T) -> Result<String, XmlParseError> {
try!(start_element(name, stack));
let value = try!(characters(stack));
try!(end_element(name, stack));
Ok(value)
}
pub fn characters<T: Peek + Next>(stack: &mut T) -> Result<String, XmlParseError> {
if let Some(XmlEvent::Characters(data)) = stack.next() {
Ok(data.to_string())
} else {
Err(XmlParseError::new("Expected characters"))
}
}
pub fn peek_at_name<T: Peek + Next>(stack: &mut T) -> Result<String, XmlParseError> {
let current = stack.peek();
if let Some(&XmlEvent::StartElement{ref name, ..}) = current {
Ok(name.local_name.to_string())
} else {
Ok("".to_string())
}
}
pub fn start_element<T: Peek + Next>(element_name: &str, stack: &mut T) -> Result<HashMap<String, String>, XmlParseError> {
let next = stack.next();
if let Some(XmlEvent::StartElement { name, attributes, .. }) = next {
if name.local_name == element_name {
let mut attr_map = HashMap::new();
for attr in attributes {
attr_map.insert(attr.name.local_name, attr.value);
}
Ok(attr_map)
} else {
Err(XmlParseError::new(&format!("START Expected {} got {}", element_name, name.local_name)))
}
} else {
Err(XmlParseError::new(&format!("Expected StartElement {}", element_name)))
}
}
pub fn end_element<T: Peek + Next>(element_name: &str, stack: &mut T) -> Result<(), XmlParseError> {
let next = stack.next();
if let Some(XmlEvent::EndElement { name, .. }) = next {
if name.local_name == element_name {
Ok(())
} else {
Err(XmlParseError::new(&format!("END Expected {} got {}", element_name, name.local_name)))
}
}else {
Err(XmlParseError::new(&format!("Expected EndElement {} got {:?}", element_name, next)))
}
}
pub fn skip_tree<T: Peek + Next>(stack: &mut T) {
let mut deep: usize = 0;
loop {
match stack.next() {
None => break,
Some(XmlEvent::StartElement { .. }) => deep += 1,
Some(XmlEvent::EndElement { ..}) => {
if deep > 1 {
deep -= 1;
} else {
break;
}
},
_ => (),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use xml::reader::*;
use std::io::Read;
use std::fs::File;
#[test]
fn peek_at_name_happy_path() {
let mut file = File::open("tests/sample-data/list_queues_with_queue.xml").unwrap();
let mut body = String::new();
let _size = file.read_to_string(&mut body);
let mut my_parser = EventReader::new(body.as_bytes());
let my_stack = my_parser.events().peekable();
let mut reader = XmlResponse::new(my_stack);
loop {
reader.next();
match peek_at_name(&mut reader) {
Ok(data) => {
if data == "QueueUrl" {
return;
}
}
Err(_) => panic!("Couldn't peek at name")
}
}
}
#[test]
fn start_element_happy_path() {
let mut file = File::open("tests/sample-data/list_queues_with_queue.xml").unwrap();
let mut body = String::new();
let _size = file.read_to_string(&mut body);
let mut my_parser = EventReader::new(body.as_bytes());
let my_stack = my_parser.events().peekable();
let mut reader = XmlResponse::new(my_stack);
reader.next();
reader.next();
match start_element("ListQueuesResult", &mut reader) {
Ok(_) => (),
Err(_) => panic!("Couldn't find start element")
}
}
#[test]
fn string_field_happy_path() {
let mut file = File::open("tests/sample-data/list_queues_with_queue.xml").unwrap();
let mut body = String::new();
let _size = file.read_to_string(&mut body);
let mut my_parser = EventReader::new(body.as_bytes());
let my_stack = my_parser.events().peekable();
let mut reader = XmlResponse::new(my_stack);
reader.next();
reader.next();
reader.next();
let my_chars = string_field("QueueUrl", &mut reader).unwrap();
assert_eq!(my_chars, "https://sqs.us-east-1.amazonaws.com/347452556413/testqueue")
}
#[test]
fn end_element_happy_path() {
let mut file = File::open("tests/sample-data/list_queues_with_queue.xml").unwrap();
let mut body = String::new();
let _size = file.read_to_string(&mut body);
let mut my_parser = EventReader::new(body.as_bytes());
let my_stack = my_parser.events().peekable();
let mut reader = XmlResponse::new(my_stack);
reader.next();
reader.next();
reader.next();
reader.next();
reader.next();
reader.next();
match end_element("ListQueuesResult", &mut reader) {
Ok(_) => (),
Err(_) => panic!("Couldn't find end element")
}
}
}