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 }
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}