use xml::reader::{EventReader, XmlEvent};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MxNode {
pub name: String,
pub value: Option<String>,
pub attributes: Vec<(String, String)>,
pub children: Vec<MxNode>,
}
impl MxNode {
pub fn parse(xml: &str) -> Option<MxNode> {
let parser = EventReader::from_str(xml);
let mut stack: Vec<MxNode> = Vec::new();
let mut root: Option<MxNode> = None;
for event in parser {
match event.ok()? {
XmlEvent::StartElement {
name, attributes, ..
} => {
let node = MxNode {
name: name.local_name,
value: None,
attributes: attributes
.into_iter()
.map(|a| (a.name.local_name, a.value))
.collect(),
children: Vec::new(),
};
stack.push(node);
}
XmlEvent::Characters(text) => {
if let Some(top) = stack.last_mut() {
let t = text.trim();
if !t.is_empty() {
top.value = Some(match top.value.take() {
Some(mut v) => {
v.push_str(t);
v
}
None => t.to_string(),
});
}
}
}
XmlEvent::EndElement { .. } => {
if let Some(node) = stack.pop() {
match stack.last_mut() {
Some(parent) => parent.children.push(node),
None => root = Some(node),
}
}
}
_ => {}
}
}
root
}
pub fn text(&self) -> Option<&str> {
self.value.as_deref()
}
pub fn attr(&self, name: &str) -> Option<&str> {
self.attributes
.iter()
.find(|(k, _)| k == name)
.map(|(_, v)| v.as_str())
}
pub fn get(&self, name: &str) -> Option<&MxNode> {
self.children.iter().find(|c| c.name == name)
}
pub fn at(&self, path: &[&str]) -> Option<&MxNode> {
let mut cur = self;
for seg in path {
cur = cur.get(seg)?;
}
Some(cur)
}
pub fn find(&self, name: &str) -> Option<&MxNode> {
for c in &self.children {
if c.name == name {
return Some(c);
}
if let Some(found) = c.find(name) {
return Some(found);
}
}
None
}
pub fn find_all<'a>(&'a self, name: &str) -> Vec<&'a MxNode> {
let mut out = Vec::new();
self.collect(name, &mut out);
out
}
fn collect<'a>(&'a self, name: &str, out: &mut Vec<&'a MxNode>) {
for c in &self.children {
if c.name == name {
out.push(c);
}
c.collect(name, out);
}
}
}