use azure_core::{
error::{Error, ErrorKind},
parsing::FromStringOptional,
};
use tracing::trace;
use xml::Element;
use xml::Xml::{CharacterNode, ElementNode};
#[inline]
pub fn traverse_single_must<'a>(
node: &'a Element,
path: &[&str],
) -> azure_core::Result<&'a Element> {
let vec = traverse(node, path, false)?;
if vec.len() > 1 {
return Err(Error::with_message(ErrorKind::Other, || {
format!("multiple node: {}", path[path.len() - 1])
}));
}
Ok(vec[0])
}
pub fn traverse_single_optional<'a>(
node: &'a Element,
path: &[&str],
) -> azure_core::Result<Option<&'a Element>> {
let vec = traverse(node, path, true)?;
if vec.len() > 1 {
return Err(Error::with_message(ErrorKind::Other, || {
format!("multiple node: {}", path[path.len() - 1])
}));
}
if vec.is_empty() {
return Ok(None);
}
Ok(Some(vec[0]))
}
#[inline]
pub fn traverse<'a>(
node: &'a Element,
path: &[&str],
ignore_empty_leaf: bool,
) -> azure_core::Result<Vec<&'a Element>> {
trace!(
"traverse(node == {:?}, path == {:?}, ignore_empty_leaf == {})",
node,
path,
ignore_empty_leaf
);
if path.is_empty() {
return Ok(vec![node]);
}
let mut curnode = node;
for (x, item) in path.iter().enumerate() {
let vec = find_subnodes(curnode, item);
if vec.is_empty() {
if (x + 1) >= path.len() && ignore_empty_leaf {
return Ok(vec);
}
return Err(Error::with_message(ErrorKind::Other, || {
format!("path not found: {}", *item)
}));
}
if vec.len() > 1 && (x + 1) < path.len() {
return Err(Error::with_message(ErrorKind::Other, || {
format!("multiple node: {}", *item)
}));
}
if (x + 1) >= path.len() {
return Ok(vec);
}
curnode = vec[0];
}
unreachable!();
}
#[inline]
pub fn find_subnodes<'a>(node: &'a Element, subnode: &str) -> Vec<&'a Element> {
node.children
.iter()
.filter(|x| match **x {
ElementNode(ref mynode) => mynode.name == subnode,
_ => false,
})
.map(|x| match *x {
ElementNode(ref mynode) => mynode,
_ => unreachable!(),
})
.collect::<Vec<_>>()
}
#[inline]
pub fn inner_text(node: &Element) -> azure_core::Result<&str> {
for child in &node.children {
match *child {
CharacterNode(ref txt) => return Ok(txt),
_ => continue,
};
}
Ok("")
}
#[inline]
pub fn cast_optional<'a, T>(node: &'a Element, path: &[&str]) -> azure_core::Result<Option<T>>
where
T: FromStringOptional<T>,
{
match traverse_single_optional(node, path)? {
Some(e) => match inner_text(e) {
Ok(txt) => Ok(Some(T::from_str_optional(txt)?)),
Err(_) => Ok(None),
},
None => Ok(None),
}
}
#[inline]
pub fn cast_must<'a, T>(node: &'a Element, path: &[&str]) -> azure_core::Result<T>
where
T: FromStringOptional<T>,
{
let node = traverse_single_must(node, path)?;
let itxt = inner_text(node)?;
T::from_str_optional(itxt)
}
#[cfg(test)]
mod test {
use xml::Element;
const XML: &str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<EnumerationResults \
ServiceEndpoint=\"http://mindrust.blob.core.windows.net/\">
\
<Containers>
<Container>
<Name>pippo</Name>
\
<Properties>
<Last-Modified>Mon, 23Nov 2015 21:12:35 \
GMT</Last-Modified>
<Etag>\"0x8D2F44ACF757699\"</Etag>
\
<LeaseStatus>unlocked</LeaseStatus>
\
<LeaseState>available</LeaseState>
\
<SomeNumber>256</SomeNumber>
</Properties>
</Container>
\
<Container>
<Name>pluto</Name>
<Properties>
\
<Last-Modified>Mon, 23Nov 2015 21:12:35 GMT</Last-Modified>
\
<Etag>\"0xAA2F44ACF757699\"</Etag>
\
<LeaseStatus>locked</LeaseStatus>
\
<LeaseState>available</LeaseState>
</Properties>
\
</Container>
</Containers>
<NextMarker />
</EnumerationResults>";
#[test]
fn test_cast_optional_1() {
let elem: Element = XML.parse().unwrap();
let sub1 = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
{
let num = super::cast_optional::<u64>(sub1[0], &["Properties", "SomeNumber"]).unwrap();
assert_eq!(Some(256u64), num);
}
{
let num2 = super::cast_optional::<u64>(sub1[1], &["Properties", "SomeNumber"]).unwrap();
assert_eq!(None, num2);
}
}
#[test]
fn test_first_1() {
let elem: Element = XML.parse().unwrap();
let sub1 = super::find_subnodes(&elem, "Containers");
assert_eq!(1, sub1.len());
let sub2 = super::find_subnodes(sub1[0], "Container");
assert_eq!(2, sub2.len());
}
#[test]
fn test_inner_2() {
let elem: Element = XML.parse().unwrap();
let mut sub = super::find_subnodes(&elem, "Containers");
sub = super::find_subnodes(sub[0], "Container");
sub = super::find_subnodes(sub[0], "Properties");
sub = super::find_subnodes(sub[0], "LeaseStatus");
if let Ok(inner) = super::inner_text(sub[0]) {
assert_eq!(inner, "unlocked");
} else {
panic!("should have found CharacterNode");
}
}
#[test]
fn test_traverse_1() {
let elem: Element = XML.parse().unwrap();
let mut res = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
res = super::traverse(res[0], &["Properties", "LeaseStatus"], false).unwrap();
if let Ok(inner) = super::inner_text(res[0]) {
assert_eq!(inner, "unlocked");
} else {
panic!("should have found CharacterNode");
}
}
#[test]
fn test_traverse_2() {
let elem: Element = XML.parse().unwrap();
let mut res = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
res = super::traverse(res[1], &["Properties", "LeaseStatus"], false).unwrap();
if let Ok(inner) = super::inner_text(res[0]) {
assert_eq!(inner, "locked");
} else {
panic!("should have found CharacterNode");
}
}
#[test]
fn test_traverse_single_must_1() {
let elem: Element = XML.parse().unwrap();
let res = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
let res_final =
super::traverse_single_must(res[1], &["Properties", "LeaseStatus"]).unwrap();
if let Ok(inner) = super::inner_text(res_final) {
assert_eq!(inner, "locked");
} else {
panic!("should have found CharacterNode");
}
}
#[test]
fn test_traverse_single_optional_1() {
let elem: Element = XML.parse().unwrap();
let res = super::traverse(&elem, &["Containers", "Container"], false).unwrap();
let res_final =
super::traverse_single_optional(res[1], &["Properties", "Pinocchio"]).unwrap();
assert_eq!(res_final, None);
}
}