use std::fmt::Display; #[derive(Debug, PartialEq, Eq, Clone)] pub struct Elements { elements: Vec, } impl Elements { pub fn get_first_tagged(&self) -> Option<&Tagged> { self.elements.iter().find_map(|element| match element { Element::Tagged(tagged_element) => Some(tagged_element), _ => None, }) } pub fn get_first_tagged_with_name(&self, tag_name: &str) -> Option<&Tagged> { self.elements.iter().find_map(|element| match element { Element::Tagged(tagged_element) if tagged_element.name == tag_name => { Some(tagged_element) } _ => None, }) } pub fn get_all_tagged_elements(&self) -> Vec<&Tagged> { self.elements .iter() .filter_map(|element| match element { Element::Tagged(tagged_element) => Some(tagged_element), _ => None, }) .collect() } pub fn get_all_tagged_elements_with_name(&self, tag_name: &str) -> Vec<&Tagged> { self.elements .iter() .filter_map(|element| match element { Element::Tagged(tagged_element) if tagged_element.name == tag_name => { Some(tagged_element) } _ => None, }) .collect() } pub fn get_all_tagged_with_name_and_attr( &self, tag_name: &str, attr_is_match: fn(&Attribute) -> bool, ) -> Vec<&Tagged> { self.elements .iter() .filter_map(|element| match element { Element::Tagged(tagged_element) if tagged_element.name == tag_name && tagged_element.attributes.iter().any(attr_is_match) => { Some(tagged_element) } _ => None, }) .collect() } pub fn get_first_text_element(&self) -> Option<&String> { self.elements.iter().find_map(|element| match element { Element::Text(text) => Some(text), _ => None, }) } pub fn get_all_text_elements(&self) -> Vec<&String> { self.elements .iter() .filter_map(|element| match element { Element::Text(text) => Some(text), _ => None, }) .collect() } pub fn has_tagged_element(&self, tag_name: &str) -> bool { self.elements.iter().any(|element| { matches!( element, Element::Tagged(tagged_element) if tagged_element.name == tag_name ) }) } } impl> From for Elements { fn from(into_iter: IntoIter) -> Self { Self { elements: into_iter.into_iter().collect(), } } } impl<'elements> IntoIterator for &'elements Elements { type IntoIter = Iter<'elements>; type Item = &'elements Element; fn into_iter(self) -> Self::IntoIter { Self::IntoIter { elements: self.elements.iter(), } } } pub struct Iter<'elements> { elements: std::slice::Iter<'elements, Element>, } impl<'elements> Iterator for Iter<'elements> { type Item = &'elements Element; fn next(&mut self) -> Option { self.elements.next() } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum Element { Tagged(Tagged), Text(String), Comment(String), } impl Display for Element { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Tagged(tagged) => tagged.fmt(formatter), Self::Text(text) => text.fmt(formatter), Self::Comment(comment) => { formatter.write_fmt(format_args!("")) } } } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct Tagged { name: String, child_elements: Elements, attributes: Vec, } impl Display for Tagged { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = &self.name; let attrs = self .attributes .iter() .map(|attr| { format!("{}=\"{}\" ", attr.key, String::from_utf8_lossy(&attr.value)) }) .collect::(); let child_elements = self .child_elements .into_iter() .map(ToString::to_string) .collect::(); formatter.write_fmt(format_args!("<{name} {attrs}>{child_elements}",)) } } impl Tagged { pub fn new( name: &Name, child_elements: ChildElements, attributes: Attrs, ) -> Self where Name: ToString, ChildElements: Into, Attrs: IntoIterator, { Self { name: name.to_string(), child_elements: child_elements.into(), attributes: attributes.into_iter().collect(), } } pub fn name(&self) -> &str { &self.name } pub fn attributes(&self) -> &[Attribute] { &self.attributes } pub fn child_elements(&self) -> &Elements { &self.child_elements } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct Attribute { pub key: String, pub value: Vec, } pub trait FromElements: Sized { type Error; fn from_elements(elements: &Elements) -> Result; }