azure_storage/
parsing_xml.rs

1use azure_core::{
2    error::{Error, ErrorKind},
3    parsing::FromStringOptional,
4};
5use tracing::trace;
6use xml::Element;
7use xml::Xml::{CharacterNode, ElementNode};
8
9#[inline]
10pub fn traverse_single_must<'a>(
11    node: &'a Element,
12    path: &[&str],
13) -> azure_core::Result<&'a Element> {
14    let vec = traverse(node, path, false)?;
15    if vec.len() > 1 {
16        return Err(Error::with_message(ErrorKind::Other, || {
17            format!("multiple node: {}", path[path.len() - 1])
18        }));
19    }
20
21    Ok(vec[0])
22}
23
24pub fn traverse_single_optional<'a>(
25    node: &'a Element,
26    path: &[&str],
27) -> azure_core::Result<Option<&'a Element>> {
28    let vec = traverse(node, path, true)?;
29    if vec.len() > 1 {
30        return Err(Error::with_message(ErrorKind::Other, || {
31            format!("multiple node: {}", path[path.len() - 1])
32        }));
33    }
34
35    if vec.is_empty() {
36        return Ok(None);
37    }
38
39    Ok(Some(vec[0]))
40}
41
42#[inline]
43pub fn traverse<'a>(
44    node: &'a Element,
45    path: &[&str],
46    ignore_empty_leaf: bool,
47) -> azure_core::Result<Vec<&'a Element>> {
48    trace!(
49        "traverse(node == {:?}, path == {:?}, ignore_empty_leaf == {})",
50        node,
51        path,
52        ignore_empty_leaf
53    );
54
55    if path.is_empty() {
56        return Ok(vec![node]);
57    }
58
59    let mut curnode = node;
60
61    for (x, item) in path.iter().enumerate() {
62        let vec = find_subnodes(curnode, item);
63        if vec.is_empty() {
64            if (x + 1) >= path.len() && ignore_empty_leaf {
65                return Ok(vec);
66            }
67            return Err(Error::with_message(ErrorKind::Other, || {
68                format!("path not found: {}", *item)
69            }));
70        }
71
72        if vec.len() > 1 && (x + 1) < path.len() {
73            return Err(Error::with_message(ErrorKind::Other, || {
74                format!("multiple node: {}", *item)
75            }));
76        }
77
78        if (x + 1) >= path.len() {
79            return Ok(vec);
80        }
81
82        curnode = vec[0];
83    }
84
85    unreachable!();
86}
87
88#[inline]
89pub fn find_subnodes<'a>(node: &'a Element, subnode: &str) -> Vec<&'a Element> {
90    node.children
91        .iter()
92        .filter(|x| match **x {
93            ElementNode(ref mynode) => mynode.name == subnode,
94            _ => false,
95        })
96        .map(|x| match *x {
97            ElementNode(ref mynode) => mynode,
98            _ => unreachable!(),
99        })
100        .collect::<Vec<_>>()
101}
102
103#[inline]
104pub fn inner_text(node: &Element) -> azure_core::Result<&str> {
105    for child in &node.children {
106        match *child {
107            CharacterNode(ref txt) => return Ok(txt),
108            _ => continue,
109        };
110    }
111
112    Ok("")
113
114    //debug!("\n!!! node == {}", node);
115    //Err(TraversingError::TextNotFound)
116}
117
118#[inline]
119pub fn cast_optional<'a, T>(node: &'a Element, path: &[&str]) -> azure_core::Result<Option<T>>
120where
121    T: FromStringOptional<T>,
122{
123    match traverse_single_optional(node, path)? {
124        Some(e) => match inner_text(e) {
125            Ok(txt) => Ok(Some(T::from_str_optional(txt)?)),
126            Err(_) => Ok(None),
127        },
128        None => Ok(None),
129    }
130}
131
132#[inline]
133pub fn cast_must<'a, T>(node: &'a Element, path: &[&str]) -> azure_core::Result<T>
134where
135    T: FromStringOptional<T>,
136{
137    let node = traverse_single_must(node, path)?;
138    let itxt = inner_text(node)?;
139    T::from_str_optional(itxt)
140}
141
142#[cfg(test)]
143mod test {
144    use xml::Element;
145
146    const XML: &str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
147<EnumerationResults \
148                               ServiceEndpoint=\"http://mindrust.blob.core.windows.net/\">
149  \
150                               <Containers>
151    <Container>
152      <Name>pippo</Name>
153      \
154                               <Properties>
155        <Last-Modified>Mon, 23Nov 2015 21:12:35 \
156                               GMT</Last-Modified>
157        <Etag>\"0x8D2F44ACF757699\"</Etag>
158        \
159                               <LeaseStatus>unlocked</LeaseStatus>
160        \
161                               <LeaseState>available</LeaseState>
162                               \
163                               <SomeNumber>256</SomeNumber>
164      </Properties>
165    </Container>
166    \
167                               <Container>
168      <Name>pluto</Name>
169      <Properties>
170        \
171                               <Last-Modified>Mon, 23Nov 2015 21:12:35 GMT</Last-Modified>
172        \
173                               <Etag>\"0xAA2F44ACF757699\"</Etag>
174        \
175                               <LeaseStatus>locked</LeaseStatus>
176        \
177                               <LeaseState>available</LeaseState>
178      </Properties>
179    \
180                               </Container>
181  </Containers>
182  <NextMarker />
183</EnumerationResults>";
184
185    #[test]
186    fn test_cast_optional_1() {
187        let elem: Element = XML.parse().unwrap();
188
189        let sub1 = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
190
191        {
192            let num = super::cast_optional::<u64>(sub1[0], &["Properties", "SomeNumber"]).unwrap();
193            assert_eq!(Some(256u64), num);
194        }
195
196        {
197            let num2 = super::cast_optional::<u64>(sub1[1], &["Properties", "SomeNumber"]).unwrap();
198            assert_eq!(None, num2);
199        }
200    }
201
202    #[test]
203    fn test_first_1() {
204        let elem: Element = XML.parse().unwrap();
205
206        let sub1 = super::find_subnodes(&elem, "Containers");
207        assert_eq!(1, sub1.len());
208
209        let sub2 = super::find_subnodes(sub1[0], "Container");
210        assert_eq!(2, sub2.len());
211    }
212
213    #[test]
214    fn test_inner_2() {
215        let elem: Element = XML.parse().unwrap();
216
217        let mut sub = super::find_subnodes(&elem, "Containers");
218        sub = super::find_subnodes(sub[0], "Container");
219        sub = super::find_subnodes(sub[0], "Properties");
220        sub = super::find_subnodes(sub[0], "LeaseStatus");
221
222        if let Ok(inner) = super::inner_text(sub[0]) {
223            assert_eq!(inner, "unlocked");
224        } else {
225            panic!("should have found CharacterNode");
226        }
227    }
228
229    #[test]
230    fn test_traverse_1() {
231        let elem: Element = XML.parse().unwrap();
232
233        let mut res = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
234        res = super::traverse(res[0], &["Properties", "LeaseStatus"], false).unwrap();
235
236        if let Ok(inner) = super::inner_text(res[0]) {
237            assert_eq!(inner, "unlocked");
238        } else {
239            panic!("should have found CharacterNode");
240        }
241    }
242
243    #[test]
244    fn test_traverse_2() {
245        let elem: Element = XML.parse().unwrap();
246
247        let mut res = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
248        res = super::traverse(res[1], &["Properties", "LeaseStatus"], false).unwrap();
249
250        if let Ok(inner) = super::inner_text(res[0]) {
251            assert_eq!(inner, "locked");
252        } else {
253            panic!("should have found CharacterNode");
254        }
255    }
256
257    #[test]
258    fn test_traverse_single_must_1() {
259        let elem: Element = XML.parse().unwrap();
260
261        let res = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
262        let res_final =
263            super::traverse_single_must(res[1], &["Properties", "LeaseStatus"]).unwrap();
264
265        if let Ok(inner) = super::inner_text(res_final) {
266            assert_eq!(inner, "locked");
267        } else {
268            panic!("should have found CharacterNode");
269        }
270    }
271
272    #[test]
273    fn test_traverse_single_optional_1() {
274        let elem: Element = XML.parse().unwrap();
275
276        let res = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
277        let res_final =
278            super::traverse_single_optional(res[1], &["Properties", "Pinocchio"]).unwrap();
279
280        assert_eq!(res_final, None);
281    }
282}