use std::io::Read; use anyhow::Result; use thiserror::Error; use xml::{attribute::OwnedAttribute, name::OwnedName, reader::XmlEvent, EventReader}; pub struct MyEventReader { depth: u32, event_reader: EventReader, } impl MyEventReader { pub fn next(&mut self) -> Result { let event = self.event_reader.next()?; if let XmlEvent::StartElement { .. } = event { self.depth += 1; } else if let XmlEvent::EndElement { .. } = event { self.depth -= 1; } Ok(event) } #[inline(always)] pub fn depth(&self) -> u32 { self.depth } } impl From> for MyEventReader { fn from(event_reader: EventReader) -> Self { MyEventReader { depth: 0, event_reader, } } } #[derive(Error, Debug, PartialEq)] pub enum ParseProjectError { #[error("XML attribute '{}' not found", format_name_from_parts(.0, .1))] AttributeNotFound(Option, String), #[error("Unexpected end of XML document")] EndOfDocument, } fn format_name_from_parts(prefix: &Option, local_name: &str) -> String { if let Some(prefix) = &prefix { format!("{}:{}", prefix, local_name) } else { local_name.into() } } pub fn format_name(name: &OwnedName) -> String { format_name_from_parts(&name.prefix, &name.local_name) } pub fn check_name(name: &OwnedName, prefix: Option<&str>, local_name: &str) -> bool { name.local_name.eq(local_name) && name.prefix_ref().eq(&prefix) } pub fn get_attribute<'a>( attributes: &'a [OwnedAttribute], prefix: Option<&str>, name: &str, ) -> Result<&'a str, ParseProjectError> { Ok(attributes .iter() .find(|attr| check_name(&attr.name, prefix, name)) .map(|attr| &attr.value[..]) .ok_or_else(|| { ParseProjectError::AttributeNotFound(prefix.map(|s| s.to_owned()), name.to_owned()) })?) } #[inline(always)] pub fn check_attribute( attributes: &[OwnedAttribute], prefix: Option<&str>, name: &str, expected_value: &str, ) -> bool { if let Ok(attr) = get_attribute(attributes, prefix, name) { return attr.eq(expected_value); } false } pub fn get_element_characters(parser: &mut MyEventReader) -> Result { let mut parts = vec![]; loop { match parser.next()? { XmlEvent::Characters(text) => { parts.push(text); } XmlEvent::EndElement { name } => { break; } _ => {} } } Ok(parts.join(" ")) } pub fn parse_element( parser: &mut MyEventReader, process_element: &mut F, ) -> Result<()> where F: FnMut(&mut MyEventReader, OwnedName, Vec) -> Result<()>, { let starting_depth = parser.depth(); loop { match parser.next()? { XmlEvent::StartElement { name, attributes, .. } => { process_element(parser, name, attributes)?; if parser.depth() == starting_depth - 1 { break; } } XmlEvent::EndElement { name } => { if parser.depth() == starting_depth - 1 { break; } } XmlEvent::EndDocument => Err(ParseProjectError::EndOfDocument)?, _ => {} } } return Ok(()); } #[macro_export] macro_rules! unwrap_err_continue { ($res:expr) => { match $res { Ok(val) => val, Err(e) => { continue; } } }; } #[macro_export] macro_rules! unwrap_opt_continue { ($res:expr) => { match $res { Some(val) => val, None => { continue; } } }; }