use xmltree::Element;
pub trait ElementExt {
fn debug(&self) -> String;
fn attr(&self, name: &str) -> crate::Result<&String>;
fn first_child(&self, name: &str) -> crate::Result<&Self>;
fn first_child_by_attr(
&self,
name: Option<&str>,
attr: &str,
value: &str,
) -> crate::Result<&Self>;
fn iter_children_with_name<'a>(
&'a self,
name: &'static str,
parent_name: Option<&'static str>,
) -> Box<dyn Iterator<Item = &'a Element> + 'a>;
}
impl ElementExt for xmltree::Element {
fn debug(&self) -> String {
use std::fmt::Write;
let mut s = "<".to_string();
if let Some(ref prefix) = self.prefix {
write!(&mut s, "{}:", prefix).unwrap();
}
write!(&mut s, "{} ", self.name).unwrap();
for (attr, value) in self.attributes.iter() {
write!(&mut s, "{}={:?} ", attr, value).unwrap();
}
write!(&mut s, "...>").unwrap();
s
}
fn attr(&self, name: &str) -> crate::Result<&String> {
self.attributes
.get(name)
.ok_or_else(|| error::MissingAttribute::new(name, self).into())
}
fn first_child(&self, name: &str) -> crate::Result<&Self> {
self.get_child(name)
.ok_or_else(|| error::MissingElement::new(name, self).into())
}
fn first_child_by_attr(
&self,
name: Option<&str>,
attr: &str,
value: &str,
) -> crate::Result<&Self> {
self.children
.iter()
.filter_map(|node| node.as_element())
.find(|c| {
if let Some(n) = name {
if n != c.name {
return false;
}
}
c.attributes.get(attr) == Some(&value.into())
})
.ok_or_else(|| {
error::MissingElement::new(
format!("<{} {}='{}'>", name.unwrap_or("???"), attr, value),
self,
)
.into()
})
}
fn iter_children_with_name<'a>(
&'a self,
name: &'static str,
parent_name: Option<&'static str>,
) -> Box<dyn Iterator<Item = &'a Element> + 'a> {
Box::new(self.children.iter().filter_map(move |node| {
if node.as_comment().is_some() {
return None;
}
let child = node.as_element().filter(|e| e.name == name);
if let Some(parent_name) = parent_name {
if child.is_none() {
log::warn!("Unhandled child element in <{parent_name}>: {child:?}");
}
}
child
}))
}
}
pub mod error {
use super::ElementExt;
pub struct MissingAttribute(String, String);
impl crate::DisplayError for MissingAttribute {
fn format(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> {
let maybe_styled_element = {
cfg_if::cfg_if! {
if #[cfg(feature = "cli")] {
use colored::Colorize;
self.1.dimmed()
} else {
&self.1
}
}
};
write!(
w,
"Missing attribute {:?} on\n {}",
self.0, maybe_styled_element,
)
}
}
impl MissingAttribute {
pub fn new<S: Into<String>>(attr: S, el: &xmltree::Element) -> Self {
MissingAttribute(attr.into(), el.debug())
}
}
pub struct MissingElement(String, String);
impl crate::DisplayError for MissingElement {
fn format(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> {
let maybe_styled_element = {
cfg_if::cfg_if! {
if #[cfg(feature = "cli")] {
use colored::Colorize;
self.1.dimmed()
} else {
&self.1
}
}
};
write!(
w,
"Missing child {:?} in\n {}",
self.0, maybe_styled_element
)
}
}
impl MissingElement {
pub fn new<S: Into<String>>(name: S, el: &xmltree::Element) -> Self {
MissingElement(name.into(), el.debug())
}
}
}