#[derive(Debug, PartialEq, Eq)]
pub struct Elements
{
    elements: Vec<Element>,
}

impl Elements
{
    pub fn get_first_tagged_element(&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_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_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<IntoIter: IntoIterator<Item = Element>> From<IntoIter> 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::Item>
    {
        self.elements.next()
    }
}

#[derive(Debug, PartialEq, Eq)]
pub enum Element
{
    Tagged(Tagged),
    Text(String),
    Comment(String),
}

#[derive(Debug, PartialEq, Eq)]
pub struct Tagged
{
    name: String,
    child_elements: Elements,
}

impl Tagged
{
    pub fn new<Name, ChildElements>(name: &Name, child_elements: ChildElements) -> Self
    where
        Name: ToString,
        ChildElements: Into<Elements>,
    {
        Self {
            name: name.to_string(),
            child_elements: child_elements.into(),
        }
    }

    pub fn name(&self) -> &str
    {
        &self.name
    }

    pub fn child_elements(&self) -> &Elements
    {
        &self.child_elements
    }
}

pub trait FromElements: Sized
{
    type Error;

    fn from_elements(elements: &Elements) -> Result<Self, Self::Error>;
}