aboutsummaryrefslogblamecommitdiffstats
path: root/src/element.rs
blob: 55b860fee144d8881d0f39552bdc1a1eaa4446ed (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                             
          
                                              

                 
 
                   
 
            
                                     
           
  
 

                                                                         
                              
                                                                 


                       




                                        
 
                                                




                                          

                                                                             
                                            
                                 

                               

 
                                                                                                 
                                            
                 
                                  
                           

 
                       
                  

                     
       
                    


                          
                       
                    
                   
                       




                                                                                                                                                                                             

                                                                                                                      




                                                                                                                                                          
                                          
                                                                                          











                                                                   



                                                        






                                                                             




                                                                  
                    


                                                          




























































































































































































































                                                                                                   
                                         



                                                                                              
                                                   

                                                     
                                
                                                            
                                                


                                                            







                                                 











                                                                     
                                                        


























































































































                                                                                               

                                                                    




























                                                                                                
                                                                          

































































                                                                                       



                                       











                                               



                                                                 


                            
                    








                                                                              
                                                                              

         

 


























                                                                        


















                                                                                                    


                                  
                                                                                                            









                                                                                                                                                                                                       
/// elements resemble a final tree, including inherited namespace information
use std::{
    collections::{HashMap, HashSet, VecDeque},
    str::FromStr,
};

use tracing::trace;

use crate::{
    error::{DeserializeError, Error},
    Result,
};

pub type DeserializeResult<T> = std::result::Result<T, DeserializeError>;

pub trait FromElement: Sized {
    fn from_element(element: Element) -> DeserializeResult<Self>;
}

pub trait IntoElement {
    fn builder(&self) -> ElementBuilder;

    fn into_element(&self) -> Element {
        self.builder().build().unwrap()
    }

    fn get_content(&self) -> VecDeque<Content> {
        let element = self.into_element();
        element.content
    }
}

// when are namespaces names chosen then if they are automatically calculated
// namespaces are held by readers and writers.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct NamespaceDeclaration {
    pub prefix: Option<String>,
    pub namespace: String,
}

// names are qualified, they contain a reference to the namespace (held within the reader/writer)
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Name {
    pub namespace: Option<String>,
    pub local_name: String,
}

#[derive(Debug, Clone)]
pub enum Content {
    Element(Element),
    Text(String),
    PI,
    Comment(String),
}

// should this be a trait?
#[derive(Debug, Clone)]
pub struct Element {
    pub name: Name,
    // namespace: Name,
    // each element once created contains the qualified namespace information for that element
    // the name contains the qualified namespace so this is unnecessary
    // namespace: String,
    // hashmap of explicit namespace declarations on the element itself only
    // possibly not needed as can be calculated at write time depending on context and qualified namespace, and for reading, element validity and namespaces are kept track of by the reader.
    // change this to custom namespace declarations only, so you can override the definition of namespaces if you wish
    pub namespace_declaration_overrides: HashSet<NamespaceDeclaration>,
    // attributes can be in a different namespace than the element. how to make sure they are valid?
    // maybe include the namespace instead of or with the prefix
    // you can calculate the prefix from the namespaced name and the current writer context
    // you can validate the prefix and calculate the namespace from the current reader context
    // this results in readers and writers being able to return qualification errors as they aren't able to create elements until every part is qualified.
    pub attributes: HashMap<Name, String>,
    // TODO: make a hashmap maybe? to be able to address parts of the content individually
    pub content: VecDeque<Content>,
}

impl Element {
    pub fn identify(&self) -> (Option<&str>, &str) {
        (self.name.namespace.as_deref(), &self.name.local_name)
    }

    pub fn check_name(&self, name: &str) -> DeserializeResult<()> {
        if self.name.local_name == name {
            Ok(())
        } else {
            return Err(DeserializeError::IncorrectName {
                expected: name.to_string(),
                found: self.name.local_name.clone(),
            });
        }
    }

    pub fn check_namespace(&self, namespace: &str) -> DeserializeResult<()> {
        if self.name.namespace.as_deref() == Some(namespace) {
            return Ok(());
        } else {
            if let Some(actual_namespace) = &self.name.namespace {
                return Err(DeserializeError::IncorrectNamespace {
                    expected: namespace.to_string(),
                    found: actual_namespace.clone(),
                });
            } else {
                return Err(DeserializeError::Unqualified {
                    expected: namespace.to_string(),
                });
            }
        }
    }

    pub fn attribute_opt<V: FromStr>(&mut self, att_name: &str) -> DeserializeResult<Option<V>> {
        if let Some(att_value) = self.attributes.remove(&Name {
            namespace: None,
            local_name: att_name.to_string(),
        }) {
            let value = <V as FromStr>::from_str(&att_value)
                .map_err(|_| DeserializeError::FromStr(att_value))?;
            return Ok(Some(value));
        } else {
            return Ok(None);
        }
    }

    pub fn attribute_opt_namespaced<V: FromStr>(
        &mut self,
        att_name: &str,
        att_namespace: &str,
    ) -> DeserializeResult<Option<V>> {
        if let Some(att_value) = self.attributes.remove(&Name {
            namespace: Some(att_namespace.to_string()),
            local_name: att_name.to_string(),
        }) {
            let value = <V as FromStr>::from_str(&att_value)
                .map_err(|_| DeserializeError::FromStr(att_value))?;
            return Ok(Some(value));
        } else {
            return Ok(None);
        }
    }

    pub fn attribute<V: FromStr>(&mut self, att_name: &str) -> DeserializeResult<V> {
        let name = Name {
            namespace: None,
            local_name: att_name.to_string(),
        };
        if let Some(att_value) = self.attributes.remove(&name) {
            let value = <V as FromStr>::from_str(&att_value)
                .map_err(|_| DeserializeError::FromStr(att_value))?;
            return Ok(value);
        } else {
            return Err(DeserializeError::MissingAttribute(name));
        }
    }

    pub fn attribute_namespaced<V: FromStr>(
        &mut self,
        att_name: &str,
        att_namespace: &str,
    ) -> DeserializeResult<V> {
        let name = Name {
            namespace: Some(att_namespace.to_string()),
            local_name: att_name.to_string(),
        };
        if let Some(att_value) = self.attributes.remove(&name) {
            let value = <V as FromStr>::from_str(&att_value)
                .map_err(|_| DeserializeError::FromStr(att_value))?;
            return Ok(value);
        } else {
            return Err(DeserializeError::MissingAttribute(name));
        }
    }

    pub fn no_more_attributes(self) -> DeserializeResult<Self> {
        if self.attributes.is_empty() {
            Ok(self)
        } else {
            Err(DeserializeError::UnexpectedAttributes(self.attributes))
        }
    }

    // for xs:any

    pub fn child_one<T: FromElement>(&mut self) -> DeserializeResult<T> {
        if let Some(position) = self.content.iter().position(|content| match content {
            Content::Element(element) => <T as FromElement>::from_element(element.clone()).is_ok(),
            Content::Text(_) => false,
            Content::PI => false,
            Content::Comment(_) => false,
        }) {
            let element = self.content.remove(position).unwrap();
            if let Content::Element(e) = element {
                return <T as FromElement>::from_element(e);
            } else {
                return Err(DeserializeError::MissingChild);
            }
        } else {
            return Err(DeserializeError::MissingChild);
        }
    }

    pub fn child_opt<T: FromElement>(&mut self) -> DeserializeResult<Option<T>> {
        if let Some(position) = self.content.iter().position(|content| match content {
            Content::Element(element) => <T as FromElement>::from_element(element.clone()).is_ok(),
            Content::Text(_) => false,
            Content::PI => false,
            Content::Comment(_) => false,
        }) {
            let element = self.content.remove(position).unwrap();
            if let Content::Element(e) = element {
                return Ok(Some(<T as FromElement>::from_element(e)?));
            } else {
                return Err(DeserializeError::MissingChild);
            }
        } else {
            return Ok(None);
        }
    }

    pub fn children<T: FromElement>(&mut self) -> DeserializeResult<Vec<T>> {
        let (children, rest): (VecDeque<_>, VecDeque<_>) = self
            .content
            .clone()
            .into_iter()
            .partition(|content| match content {
                Content::Element(element) => {
                    <T as FromElement>::from_element(element.clone()).is_ok()
                }
                Content::Text(_) => false,
                Content::PI => false,
                Content::Comment(_) => false,
            });
        self.content = rest;
        let children: Vec<T> = children
            .into_iter()
            .map(|content| {
                let child = match content {
                    Content::Element(element) => <T as FromElement>::from_element(element).ok(),
                    Content::Text(_) => None,
                    Content::PI => None,
                    Content::Comment(_) => None,
                }
                .unwrap();
                child
            })
            .collect();
        Ok(children)
    }

    pub fn value<V: FromStr>(&mut self) -> DeserializeResult<V> {
        if let Some(position) = self.content.iter().position(|content| match content {
            Content::Element(_) => false,
            Content::Text(s) => <V as FromStr>::from_str(s).is_ok(),
            Content::PI => false,
            Content::Comment(_) => false,
        }) {
            let element = self.content.remove(position).unwrap();
            if let Content::Text(v) = element {
                return Ok(<V as FromStr>::from_str(&v).ok().unwrap());
            } else {
                panic!("infallible")
            }
        } else {
            return Err(DeserializeError::MissingValue);
        }
    }

    pub fn value_opt<V: FromStr>(&mut self) -> DeserializeResult<Option<V>> {
        if let Some(position) = self.content.iter().position(|content| match content {
            Content::Element(_) => false,
            Content::Text(s) => <V as FromStr>::from_str(s).is_ok(),
            Content::PI => false,
            Content::Comment(_) => false,
        }) {
            let element = self.content.remove(position).unwrap();
            if let Content::Text(v) = element {
                return Ok(<V as FromStr>::from_str(&v).ok());
            } else {
                panic!("infallible")
            }
        } else {
            return Ok(None);
        }
    }

    // for xs:sequence

    pub fn pop_child_one<T: FromElement>(&mut self) -> DeserializeResult<T> {
        loop {
            let child = self
                .content
                .pop_front()
                .ok_or(DeserializeError::MissingChild)?;
            match child {
                Content::Element(element) => return Ok(<T as FromElement>::from_element(element)?),
                Content::Text(_) => {
                    return Err(DeserializeError::UnexpectedContent(self.content.clone()))
                }
                Content::PI => {}
                Content::Comment(_) => {}
            }
        }
    }

    pub fn pop_child_opt<T: FromElement>(&mut self) -> DeserializeResult<Option<T>> {
        loop {
            let child = self.content.pop_front();
            if let Some(child) = child {
                match child {
                    Content::Element(element) => {
                        return Ok(Some(<T as FromElement>::from_element(element)?))
                    }
                    Content::Text(_) => {
                        return Err(DeserializeError::UnexpectedContent(self.content.clone()))
                    }
                    Content::PI => {}
                    Content::Comment(_) => {}
                }
            } else {
                return Ok(None);
            }
        }
    }

    pub fn pop_children<T: FromElement>(&mut self) -> DeserializeResult<Vec<T>> {
        let mut children = Vec::new();
        loop {
            let child = self.content.front();
            trace!("child: {:?}", child);
            if let Some(child) = child {
                match child {
                    Content::Element(element) => {
                        if let Ok(child) = <T as FromElement>::from_element(element.clone()) {
                            trace!("parsed child");
                            children.push(child);
                            self.content.pop_front();
                        } else {
                            trace!("failed to parse child");
                            return Ok(children);
                        }
                    }
                    Content::Text(_) => return Ok(children),
                    Content::PI => {
                        self.content.pop_front();
                        continue;
                    }
                    Content::Comment(_) => {
                        self.content.pop_front();
                        continue;
                    }
                }
            } else {
                return Ok(children);
            }
        }
    }

    pub fn pop_value<V: FromStr>(&mut self) -> DeserializeResult<V> {
        loop {
            let child = self
                .content
                .pop_front()
                .ok_or(DeserializeError::MissingValue)?;
            match child {
                Content::Element(_) => {
                    return Err(DeserializeError::UnexpectedContent(self.content.clone()))
                }
                Content::Text(t) => {
                    return Ok(
                        <V as FromStr>::from_str(&t).map_err(|_| DeserializeError::FromStr(t))?
                    )
                }
                Content::PI => {}
                Content::Comment(_) => {}
            }
        }
    }

    pub fn pop_value_opt<V: FromStr>(&mut self) -> DeserializeResult<Option<V>> {
        loop {
            let child = self.content.pop_front();
            if let Some(child) = child {
                match child {
                    Content::Element(_) => {
                        return Err(DeserializeError::UnexpectedContent(self.content.clone()))
                    }
                    Content::Text(t) => {
                        return Ok(Some(
                            <V as FromStr>::from_str(&t)
                                .map_err(|_| DeserializeError::FromStr(t))?,
                        ))
                    }
                    Content::PI => {}
                    Content::Comment(_) => {}
                }
            } else {
                return Ok(None);
            }
        }
    }

    pub fn no_more_content(self) -> DeserializeResult<Self> {
        if self
            .content
            .iter()
            .filter(|content| match content {
                Content::Element(_) => true,
                Content::Text(_) => true,
                Content::PI => false,
                Content::Comment(_) => false,
            })
            .collect::<Vec<_>>()
            .is_empty()
        {
            Ok(self)
        } else {
            Err(DeserializeError::UnexpectedContent(self.content))
        }
    }

    pub fn builder(name: impl ToString, namespace: Option<impl ToString>) -> ElementBuilder {
        ElementBuilder::new(name, namespace)
    }
}

pub struct ElementBuilder {
    name: Name,
    namespace_declaration_overrides: Vec<NamespaceDeclaration>,
    attributes: Vec<(Name, String)>,
    content: Vec<ContentBuilder>,
}

impl ElementBuilder {
    pub fn new(name: impl ToString, namespace: Option<impl ToString>) -> Self {
        Self {
            name: Name {
                namespace: namespace.map(|namespace| namespace.to_string()),
                local_name: name.to_string(),
            },
            namespace_declaration_overrides: Vec::new(),
            attributes: Vec::new(),
            content: Vec::new(),
        }
    }

    pub fn push_namespace_declaration_override(
        mut self,
        prefix: Option<impl ToString>,
        namespace: impl ToString,
    ) -> Self {
        self.namespace_declaration_overrides
            .push(NamespaceDeclaration {
                prefix: prefix.map(|prefix| prefix.to_string()),
                namespace: namespace.to_string(),
            });
        self
    }

    pub fn push_attribute<N: ToString, V: ToString>(mut self, name: N, value: V) -> Self {
        self.attributes.push((
            // TODO: make sure name is a valid name, same for prefixes
            Name {
                namespace: None,
                local_name: name.to_string(),
            },
            value.to_string(),
        ));
        self
    }

    pub fn push_attribute_namespaced(
        mut self,
        namespace: impl ToString,
        name: impl ToString,
        value: impl ToString,
    ) -> Self {
        self.attributes.push((
            Name {
                namespace: Some(namespace.to_string()),
                local_name: name.to_string(),
            },
            value.to_string(),
        ));
        self
    }

    pub fn push_child(mut self, child: impl IntoElement) -> Self {
        self.content.push(ContentBuilder::Element(child.builder()));
        self
    }

    pub fn push_text(mut self, text: impl ToString) -> Self {
        self.content.push(ContentBuilder::Text(text.to_string()));
        self
    }

    pub fn push_attribute_opt(self, name: impl ToString, value: Option<impl ToString>) -> Self {
        if let Some(value) = value {
            self.push_attribute(name, value)
        } else {
            self
        }
    }

    pub fn push_attribute_opt_namespaced(
        self,
        namespace: impl ToString,
        name: impl ToString,
        value: Option<impl ToString>,
    ) -> Self {
        if let Some(value) = value {
            self.push_attribute_namespaced(namespace, name, value)
        } else {
            self
        }
    }

    pub fn push_child_opt(self, child: Option<impl IntoElement>) -> Self {
        if let Some(child) = child {
            self.push_child(child)
        } else {
            self
        }
    }

    pub fn push_text_opt(self, text: Option<impl ToString>) -> Self {
        if let Some(text) = text {
            self.push_text(text)
        } else {
            self
        }
    }

    pub fn push_content(mut self, content: ContentBuilder) -> Self {
        self.content.push(content);
        self
    }

    pub fn push_children(self, children: Vec<impl IntoContent>) -> Self {
        let mut element_builder = self;
        for child in children {
            element_builder = element_builder.push_content(child.builder())
        }
        element_builder
    }

    pub fn build(&self) -> Result<Element> {
        let mut namespace_declaration_overrides = HashSet::new();
        for namespace_declaration in &self.namespace_declaration_overrides {
            if !namespace_declaration_overrides.insert(namespace_declaration.clone()) {
                return Err(Error::DuplicateNameSpaceDeclaration(
                    namespace_declaration.clone(),
                ));
            }
        }

        let mut attributes = HashMap::new();
        for (att_name, att_value) in &self.attributes {
            if attributes
                .insert(att_name.clone(), att_value.to_string())
                .is_some()
            {
                // TODO: better error
                return Err(Error::DuplicateAttribute(att_name.local_name.to_string()));
            }
        }

        let content: Result<VecDeque<Content>> = self
            .content
            .iter()
            .map(|content_builder| -> Result<Content> { Ok(content_builder.build()?) })
            .collect();
        let content = content?;

        Ok(Element {
            name: self.name.clone(),
            namespace_declaration_overrides,
            attributes,
            content,
        })
    }
}

pub trait IntoContent {
    fn into_content(&self) -> Content {
        self.builder().build().unwrap()
    }

    fn builder(&self) -> ContentBuilder;
}

impl<T> IntoContent for T
where
    T: IntoElement,
{
    fn builder(&self) -> ContentBuilder {
        ContentBuilder::Element(self.builder())
    }
}

pub trait FromContent: Sized {
    fn from_content(content: Content) -> DeserializeResult<Self>;
}

pub enum ContentBuilder {
    Element(ElementBuilder),
    Text(String),
    Comment(String),
}

impl ContentBuilder {
    pub fn build(&self) -> Result<Content> {
        match self {
            ContentBuilder::Element(element_builder) => {
                Ok(Content::Element(element_builder.build()?))
            }
            ContentBuilder::Text(text) => Ok(Content::Text(text.to_string())),
            ContentBuilder::Comment(s) => Ok(Content::Comment(s.to_string())),
        }
    }
}

pub fn escape_str(s: &str) -> String {
    let mut string = String::new();
    for str in s.split_inclusive(|c| c == '<' || c == '&' || c == '>') {
        if let Some(str) = str.strip_suffix('<') {
            if !str.is_empty() {
                string.push_str(str)
            }
            string.push_str("&lt;");
        } else if let Some(str) = str.strip_suffix('&') {
            if !str.is_empty() {
                string.push_str(str)
            }
            string.push_str("&amp;");
        } else if let Some(str) = str.strip_suffix('>') {
            if !str.is_empty() {
                string.push_str(str)
            }
            string.push_str("&gt;");
        } else {
            if !str.is_empty() {
                string.push_str(str)
            }
        }
    }
    string
}

// impl<'s> TryFrom<xml::Element<'s>> for Element<'s> {
//     type Error = Error;

//     fn try_from(xml_element: xml::Element) -> Result<Self, Self::Error> {
//         match &xml_element {
//             xml::Element::Empty(empty_elem_tag) => {
//                 let namespace_decl;
//                 let attributes;
//                 empty_elem_tag
//                     .attributes
//                     .into_iter()
//                     .filter(|attribute| matches!(attribute, Attribute::NamespaceDeclaration(_)));
//                 todo!()
//             }
//             xml::Element::NotEmpty(stag, content, etag) => todo!(),
//         }
//     }
// }

// example of deriving an element:

// #[derive(XMLWrite, XMLRead)]
// #[peanuts(xmlns = "jabber:client", xmlns:stream = "http://etherx.jabber.org/streams", prefix = "stream")]
// pub struct Stream {
//     from: JID,
//     id: String,
//     to: JID,
//     version: String,
//     #[peanuts(namespace = "http://www.w3.org/XML/1998/namespace")]
//     lang: Lang,
// }

// note: if an element name has a prefix all unprefixed attributes are qualified by the namespace of the prefix, so in this example from's Name's namespace would be "http://etherx.jabber.org/streams"