use std::iter::Peekable;
use std::num::ParseIntError;
use std::collections::HashMap;
use xml::reader::{Events, XmlEvent};
use xml;
#[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 [u8]>>;
pub trait Peek {
fn peek(&mut self) -> Option<&Result<XmlEvent, xml::reader::Error>>;
}
pub trait Next {
fn next(&mut self) -> Option<Result<XmlEvent, xml::reader::Error>>;
}
pub struct XmlResponse<'b> {
xml_stack: Peekable<Events<&'b [u8]>>, }
impl<'b> XmlResponse<'b> {
pub fn new(stack: Peekable<Events<&'b [u8]>>) -> XmlResponse {
XmlResponse { xml_stack: stack }
}
}
impl<'b> Peek for XmlResponse<'b> {
fn peek(&mut self) -> Option<&Result<XmlEvent, xml::reader::Error>> {
while let Some(&Ok(XmlEvent::Whitespace(_))) = self.xml_stack.peek() {
self.xml_stack.next();
}
self.xml_stack.peek()
}
}
impl<'b> Next for XmlResponse<'b> {
fn next(&mut self) -> Option<Result<XmlEvent, xml::reader::Error>> {
let mut maybe_event;
loop {
maybe_event = self.xml_stack.next();
match maybe_event {
Some(Ok(XmlEvent::Whitespace(_))) => {}
_ => break,
}
}
maybe_event
}
}
impl From<ParseIntError> for XmlParseError {
fn from(_e: ParseIntError) -> XmlParseError {
XmlParseError::new("ParseIntError")
}
}
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> {
{
let current = stack.peek();
if let Some(&Ok(XmlEvent::EndElement { .. })) = current {
return Ok("".to_string());
}
}
if let Some(Ok(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(&Ok(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(Ok(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 {} got {:#?}", element_name, next)))
}
}
pub fn end_element<T: Peek + Next>(element_name: &str, stack: &mut T) -> Result<(), XmlParseError> {
let next = stack.next();
if let Some(Ok(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(Ok(XmlEvent::StartElement { .. })) => deep += 1,
Some(Ok(XmlEvent::EndElement { .. })) => {
if deep > 1 {
deep -= 1;
} else {
break;
}
}
_ => (),
}
}
}
pub fn find_start_element<T: Peek + Next>(stack: &mut T) {
loop {
match stack.peek() {
Some(&Ok(XmlEvent::StartElement { .. })) => break,
Some(&Ok(_)) => {
stack.next().unwrap().unwrap();
},
Some(&Err(_)) => break,
None => break,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use xml::reader::EventReader;
use std::io::Read;
use std::fs::File;
#[test]
fn peek_at_name_happy_path() {
let mut file = File::open("test_resources/list_queues_with_queue.xml").unwrap();
let mut body = String::new();
let _size = file.read_to_string(&mut body);
let my_parser = EventReader::new(body.as_bytes());
let my_stack = my_parser.into_iter().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("test_resources/list_queues_with_queue.xml").unwrap();
let mut body = String::new();
let _size = file.read_to_string(&mut body);
let my_parser = EventReader::new(body.as_bytes());
let my_stack = my_parser.into_iter().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("test_resources/list_queues_with_queue.xml").unwrap();
let mut body = String::new();
let _size = file.read_to_string(&mut body);
let my_parser = EventReader::new(body.as_bytes());
let my_stack = my_parser.into_iter().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("test_resources/list_queues_with_queue.xml").unwrap();
let mut body = String::new();
let _size = file.read_to_string(&mut body);
let my_parser = EventReader::new(body.as_bytes());
let my_stack = my_parser.into_iter().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"),
}
}
#[test]
fn test_find_start_element() {
let body = include_bytes!("../test_resources/list_queues_with_queue.xml");
let parser = EventReader::new(&body[..]);
let stack = parser.into_iter().peekable();
let mut reader = XmlResponse::new(stack);
find_start_element(&mut reader);
assert_eq!(peek_at_name(&mut reader).unwrap(), "ListQueuesResponse");
find_start_element(&mut reader);
assert_eq!(peek_at_name(&mut reader).unwrap(), "ListQueuesResponse");
}
}