assemblylift_iomod_s3_guest/xml_util/
util.rs

1//! Tools for handling XML from AWS with helper functions for testing.
2//!
3//! Wraps an XML stack via traits.
4//! Also provides a method of supplying an XML stack from a file for testing purposes.
5
6use std::collections::HashMap;
7use std::io;
8use std::iter::Peekable;
9use std::num::ParseIntError;
10use xml;
11use xml::reader::{EventReader, Events, ParserConfig, XmlEvent};
12use xml::writer::EventWriter;
13
14/// generic Error for XML parsing
15#[derive(Debug)]
16pub struct XmlParseError(pub String);
17
18impl XmlParseError {
19    pub fn new(msg: &str) -> XmlParseError {
20        XmlParseError(msg.to_string())
21    }
22}
23
24/// syntactic sugar for the XML event stack we pass around
25pub type XmlStack<'a> = Peekable<Events<&'a [u8]>>;
26
27/// Peek at next items in the XML stack
28pub trait Peek {
29    fn peek(&mut self) -> Option<&Result<XmlEvent, xml::reader::Error>>;
30}
31
32/// Move to the next part of the XML stack
33pub trait Next {
34    fn next(&mut self) -> Option<Result<XmlEvent, xml::reader::Error>>;
35}
36
37/// Wraps the Hyper Response type
38pub struct XmlResponse<'b> {
39    xml_stack: Peekable<Events<&'b [u8]>>, // refactor to use XmlStack type?
40}
41
42impl<'b> XmlResponse<'b> {
43    pub fn new(stack: Peekable<Events<&'b [u8]>>) -> XmlResponse {
44        XmlResponse { xml_stack: stack }
45    }
46}
47
48impl<'b> Peek for XmlResponse<'b> {
49    fn peek(&mut self) -> Option<&Result<XmlEvent, xml::reader::Error>> {
50        while let Some(&Ok(XmlEvent::Whitespace(_))) = self.xml_stack.peek() {
51            self.xml_stack.next();
52        }
53        self.xml_stack.peek()
54    }
55}
56
57impl<'b> Next for XmlResponse<'b> {
58    fn next(&mut self) -> Option<Result<XmlEvent, xml::reader::Error>> {
59        let mut maybe_event;
60        loop {
61            maybe_event = self.xml_stack.next();
62            match maybe_event {
63                Some(Ok(XmlEvent::Whitespace(_))) => {}
64                _ => break,
65            }
66        }
67        maybe_event
68    }
69}
70
71impl From<ParseIntError> for XmlParseError {
72    fn from(_e: ParseIntError) -> XmlParseError {
73        XmlParseError::new("ParseIntError")
74    }
75}
76
77/// return a string field with the right name or throw a parse error
78pub fn string_field<T: Peek + Next>(name: &str, stack: &mut T) -> Result<String, XmlParseError> {
79    start_element(name, stack)?;
80    let value = characters(stack)?;
81    end_element(name, stack)?;
82    Ok(value)
83}
84
85pub fn write_characters_element<W>(
86    writer: &mut EventWriter<W>,
87    name: &str,
88    value_str: &str,
89) -> Result<(), xml::writer::Error>
90where
91    W: io::Write,
92{
93    writer.write(xml::writer::XmlEvent::start_element(name))?;
94    writer.write(xml::writer::XmlEvent::characters(value_str))?;
95    writer.write(xml::writer::XmlEvent::end_element())
96}
97
98pub fn deserialize_primitive<T: Peek + Next, U>(
99    tag_name: &str,
100    stack: &mut T,
101    deserialize: fn(String) -> Result<U, XmlParseError>,
102) -> Result<U, XmlParseError> {
103    start_element(tag_name, stack)?;
104    let obj = deserialize(characters(stack)?)?;
105    end_element(tag_name, stack)?;
106
107    Ok(obj)
108}
109
110/// return some XML Characters
111pub fn characters<T: Peek + Next>(stack: &mut T) -> Result<String, XmlParseError> {
112    {
113        // Lexical lifetime
114        // Check to see if the next element is an end tag.
115        // If it is, return an empty string.
116        let current = stack.peek();
117        if let Some(&Ok(XmlEvent::EndElement { .. })) = current {
118            return Ok("".to_string());
119        }
120    }
121    match stack.next() {
122        Some(Ok(XmlEvent::Characters(data))) | Some(Ok(XmlEvent::CData(data))) => Ok(data),
123        _ => Err(XmlParseError::new("Expected characters")),
124    }
125}
126
127/// get the name of the current element in the stack.  throw a parse error if it's not a `StartElement`
128pub fn peek_at_name<T: Peek + Next>(stack: &mut T) -> Result<String, XmlParseError> {
129    let current = stack.peek();
130    if let Some(&Ok(XmlEvent::StartElement { ref name, .. })) = current {
131        Ok(name.local_name.to_string())
132    } else {
133        Ok("".to_string())
134    }
135}
136
137/// consume a `StartElement` with a specific name or throw an `XmlParseError`
138pub fn start_element<T: Peek + Next>(
139    element_name: &str,
140    stack: &mut T,
141) -> Result<HashMap<String, String>, XmlParseError> {
142    let next = stack.next();
143
144    if let Some(Ok(XmlEvent::StartElement {
145        name, attributes, ..
146    })) = next
147    {
148        if name.local_name == element_name {
149            let mut attr_map = HashMap::new();
150            for attr in attributes {
151                attr_map.insert(attr.name.local_name, attr.value);
152            }
153            Ok(attr_map)
154        } else {
155            Err(XmlParseError::new(&format!(
156                "START Expected {} got {}",
157                element_name, name.local_name
158            )))
159        }
160    } else {
161        Err(XmlParseError::new(&format!(
162            "Expected StartElement {} got {:#?}",
163            element_name, next
164        )))
165    }
166}
167
168/// consume an `EndElement` with a specific name or throw an `XmlParseError`
169pub fn end_element<T: Peek + Next>(element_name: &str, stack: &mut T) -> Result<(), XmlParseError> {
170    let next = stack.next();
171    if let Some(Ok(XmlEvent::EndElement { name, .. })) = next {
172        if name.local_name == element_name {
173            Ok(())
174        } else {
175            Err(XmlParseError::new(&format!(
176                "END Expected {} got {}",
177                element_name, name.local_name
178            )))
179        }
180    } else {
181        Err(XmlParseError::new(&format!(
182            "Expected EndElement {} got {:?}",
183            element_name, next
184        )))
185    }
186}
187
188/// skip a tag and all its children
189pub fn skip_tree<T: Peek + Next>(stack: &mut T) {
190    let mut deep: usize = 0;
191
192    loop {
193        match stack.next() {
194            None => break,
195            Some(Ok(XmlEvent::StartElement { .. })) => deep += 1,
196            Some(Ok(XmlEvent::EndElement { .. })) => {
197                if deep > 1 {
198                    deep -= 1;
199                } else {
200                    break;
201                }
202            }
203            _ => (),
204        }
205    }
206}
207
208/// skip all elements until a start element is encountered
209///
210/// Errors and end-of-stream are ignored.
211pub fn find_start_element<T: Peek + Next>(stack: &mut T) {
212    loop {
213        match stack.peek() {
214            Some(&Ok(XmlEvent::StartElement { .. })) => break,
215            Some(&Ok(_)) => {
216                stack.next().unwrap().unwrap();
217            }
218            Some(&Err(_)) => break,
219            None => break,
220        }
221    }
222}
223
224pub fn deserialize_elements<T, S, F>(
225    tag_name: &str,
226    stack: &mut T,
227    mut handle_element: F,
228) -> Result<S, XmlParseError>
229where
230    T: Peek + Next,
231    S: Default,
232    F: FnMut(&str, &mut T, &mut S) -> Result<(), XmlParseError>,
233{
234    let mut obj = S::default();
235
236    start_element(tag_name, stack)?;
237
238    loop {
239        match stack.peek() {
240            Some(&Ok(XmlEvent::EndElement { .. })) => break,
241            Some(&Ok(XmlEvent::StartElement { ref name, .. })) => {
242                let local_name = name.local_name.to_owned();
243                handle_element(&local_name, stack, &mut obj)?;
244            }
245            _ => {
246                stack.next();
247            }
248        }
249    }
250
251    end_element(tag_name, stack)?;
252
253    Ok(obj)
254}