summaryrefslogblamecommitdiffstats
path: root/src/stanza/mod.rs
blob: 8251422fc73be3cf410caa723a014168939c9dcf (plain) (tree)
1
2
3
4
5
6
7
8
9

                                    

             
             
               
 

                               
                                                                        
                                     
                                           


                                          
                       
 

































































                                                                                               
                       

































                                                                                               







                                                          
                                                                        








                                       


                                             


                                                                             


                                                            














                                                                            
                                  








                                                                                            
                                  










                                                                             










                                                            



                                                                  


                                                                                      
                 


                                                 


                                                        
                             




                                      
                               






                                                            
                                    





                                                                  
                                   




                                                                     
 















                                                                                     
         
                                     
     

 










                                                      
     

 


                           

                     
 
// use quick_xml::events::BytesDecl;

pub mod bind;
pub mod iq;
pub mod sasl;
pub mod stream;

use std::collections::BTreeMap;

// const DECLARATION: BytesDecl<'_> = BytesDecl::new("1.0", None, None);
use async_recursion::async_recursion;
use quick_xml::events::{BytesStart, Event};
use quick_xml::{Reader, Writer};
use tokio::io::{AsyncBufRead, AsyncWrite};

use crate::JabberError;

// #[derive(Clone, Debug)]
// pub struct EventTree<'e> {
//     pub event: Event<'e>,
//     pub children: Option<Vec<Element<'e>>>,
// }

pub type Prefix<'s> = Option<&'s str>;

#[derive(Clone, Debug)]
/// represents an xml element as a tree of nodes
pub struct Element<'s> {
    /// element prefix
    /// e.g. `foo` in `<foo:bar />`.
    prefix: Option<&'s str>,
    /// qualifying namespace
    /// an element must be qualified by a namespace
    /// e.g. for `<stream:features>` in
    /// ```
    /// <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
    ///     <stream:features>
    ///         <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
    ///         <compression xmlns='http://jabber.org/features/compress'>
    ///             <method>zlib</method>
    ///             <method>lzw</method>
    ///         </compression>
    ///     </stream:features>
    /// </stream:stream>
    /// ```
    /// would be `"http://etherx.jabber.org/streams"` but for
    /// ```
    /// <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
    ///     <features>
    ///         <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
    ///         <compression xmlns='http://jabber.org/features/compress'>
    ///             <method>zlib</method>
    ///             <method>lzw</method>
    ///         </compression>
    ///     </features>
    /// </stream:stream>
    /// ```
    /// would be `"jabber:client"`
    namespace: &'s str,
    /// element name
    /// e.g. `bar` in `<foo:bar />`.
    name: &'s str,
    /// all namespaces applied to element
    /// e.g. for `<bind>` in
    /// ```
    /// <stream:features xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
    ///     <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
    ///     <compression xmlns='http://jabber.org/features/compress'>
    ///         <method>zlib</method>
    ///         <method>lzw</method>
    ///     </compression>
    /// </stream:features>
    /// ```
    /// would be `[(None, "urn:ietf:params:xml:ns:xmpp-bind")]` despite
    /// `(Some("stream"), "http://etherx.jabber.org/streams")` also being available
    namespaces: Box<BTreeMap<Option<&'s str>, &'s str>>,
    /// element attributes
    attributes: Box<BTreeMap<&'s str, &'s str>>,
    // children elements namespaces contain their parents' namespaces
    ///
    children: Option<Box<Vec<Node<'s>>>>,
}

#[derive(Clone, Debug)]
pub enum Node<'s> {
    Element(Element<'s>),
    Text(&'s str),
}

impl<'s> From<&Element<'s>> for Event<'s> {
    fn from(element: &Element<'s>) -> Self {
        let event;
        if let Some(prefix) = element.prefix {
            event = BytesStart::new(format!("{}:{}", prefix, element.name));
        } else {
            event = BytesStart::new(element.name);
        }

        event
        
        let event = event.with_attributes(element.attributes.into_iter());

        match element.children.is_none() {
            true => return Event::Empty(event),
            false => return Event::Start(event),
        }
    }
}

impl<'s> Element<'s> {
    /// returns the namespace which applies to the current element, e.g. for
    /// `<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>`
    /// it will be `http://etherx.jabber.org/streams` but for
    /// `<stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>`
    /// it will be `jabber:client`.
    pub fn get_namespace(&self) -> &str {
        self.namespace
    }
}

impl<'e: 'async_recursion, 'async_recursion> Element<'e> {
    pub fn write<'life0, W: AsyncWrite + Unpin + Send>(
        &'async_recursion self,
        writer: &'life0 mut Writer<W>,
    ) -> ::core::pin::Pin<
        Box<
            dyn ::core::future::Future<Output = Result<(), JabberError>>
                + 'async_recursion
                + ::core::marker::Send,
        >,
    >
    where
        W: 'async_recursion,
        'life0: 'async_recursion,
    {
        Box::pin(async move {
            match &self.children.is_empty() {
                true => {}
            }
            match &self.event {
                Event::Start(e) => {
                    writer.write_event_async(Event::Start(e.clone())).await?;
                    if let Some(children) = &self.children {
                        for e in children {
                            e.write(writer).await?;
                        }
                    }
                    writer.write_event_async(Event::End(e.to_end())).await?;
                    return Ok(());
                }
                e => Ok(writer.write_event_async(e).await?),
            }
        })
    }
}

impl<'e> Element<'e> {
    pub async fn write_start<W: AsyncWrite + Unpin + Send>(
        &self,
        writer: &mut Writer<W>,
    ) -> Result<(), JabberError> {
        match self.event.as_ref() {
            Event::Start(e) => Ok(writer.write_event_async(Event::Start(e.clone())).await?),
            e => Err(ElementError::NotAStart(e.clone().into_owned()).into()),
        }
    }

    pub async fn write_end<W: AsyncWrite + Unpin + Send>(
        &self,
        writer: &mut Writer<W>,
    ) -> Result<(), JabberError> {
        match self.event.as_ref() {
            Event::Start(e) => Ok(writer
                .write_event_async(Event::End(e.clone().to_end()))
                .await?),
            e => Err(ElementError::NotAStart(e.clone().into_owned()).into()),
        }
    }

    #[async_recursion]
    pub async fn read<R: AsyncBufRead + Unpin + Send>(
        reader: &mut Reader<R>,
    ) -> Result<Self, JabberError> {
        let element = Self::read_recursive(reader)
            .await?
            .ok_or(JabberError::UnexpectedEnd);
        element
    }

    #[async_recursion]
    async fn read_recursive<R: AsyncBufRead + Unpin + Send>(
        reader: &mut Reader<R>,
    ) -> Result<Option<Self>, JabberError> {
        let mut buf = Vec::new();
        let event = reader.read_event_into_async(&mut buf).await?;
        match event {
            Event::Start(e) => {
                let mut children_vec = Vec::new();
                while let Some(sub_element) = Element::read_recursive(reader).await? {
                    children_vec.push(sub_element)
                }
                let mut children = None;
                if !children_vec.is_empty() {
                    children = Some(children_vec)
                }
                Ok(Some(Self {
                    event: Event::Start(e.into_owned()),
                    children,
                }))
            }
            Event::End(_) => Ok(None),
            e => Ok(Some(Self {
                event: e.into_owned(),
                children: None,
            })),
        }
    }

    #[async_recursion]
    pub async fn read_start<R: AsyncBufRead + Unpin + Send>(
        reader: &mut Reader<R>,
    ) -> Result<Self, JabberError> {
        let mut buf = Vec::new();
        let event = reader.read_event_into_async(&mut buf).await?;
        match event {
            Event::Start(e) => {
                return Ok(Self {
                    event: Event::Start(e.into_owned()),
                    children: None,
                })
            }
            e => Err(ElementError::NotAStart(e.into_owned()).into()),
        }
    }

    /// if there is only one child in the vec of children, will return that element
    pub fn child<'p>(&'p self) -> Result<&'p Element<'e>, ElementError<'static>> {
        if let Some(children) = &self.children {
            if children.len() == 1 {
                return Ok(&children[0]);
            } else {
                return Err(ElementError::MultipleChildren);
            }
        }
        Err(ElementError::NoChildren)
    }

    /// returns reference to children
    pub fn children<'p>(&'p self) -> Result<&'p Vec<Element<'e>>, ElementError<'e>> {
        if let Some(children) = &self.children {
            return Ok(children);
        }
        Err(ElementError::NoChildren)
    }
}

pub trait IntoElement<'e> {
    fn event(&self) -> Event<'e>;
    fn children(&self) -> Option<Vec<Element<'e>>>;
}

impl<'e, T: IntoElement<'e>> From<T> for Element<'e> {
    fn from(value: T) -> Self {
        Element {
            event: value.event(),
            children: value.children(),
        }
    }
}

#[derive(Debug)]
pub enum ElementError<'e> {
    NotAStart(Event<'e>),
    NoChildren,
    MultipleChildren,
}