aboutsummaryrefslogblamecommitdiffstats
path: root/stanza/src/client/message.rs
blob: 78258ca6f196ba0ee86c4157c9b35bc6ef73aeac (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                      
             




                                        

                                  

                             

                            


                            

                 
                                
                    


                           
                                              

                             
               


                                 

                                

                                 

                                

                                

 














                                                                                        


                                         


                                           


                                        


                                         








                    

                                        

                                        

                                        

                                        





                                                           
                                                              












                                                                             




                                                                 


                                                                   


                                                                


                                                                 
               


     
                                                     




                      
              


           


























                                                               
                       
                 
                             
                         
                             

 



















                                                                                                 
                       




                            



















                                                                                                 
                       
                   


                           



















                                                                                                 
use std::str::FromStr;

use jid::JID;
use peanuts::{
    element::{FromElement, IntoElement},
    DeserializeError, Element, XML_NS,
};

#[cfg(feature = "xep_0060")]
use crate::xep_0060::event::Event;
#[cfg(feature = "xep_0131")]
use crate::xep_0131::Headers;
#[cfg(feature = "xep_0172")]
use crate::xep_0172::Nick;
#[cfg(feature = "xep_0203")]
use crate::xep_0203::Delay;

use super::XMLNS;

#[derive(Debug, Clone, Default)]
pub struct Message {
    pub from: Option<JID>,
    pub id: Option<String>,
    pub to: Option<JID>,
    // can be omitted, if so default to normal
    pub r#type: MessageType,
    pub lang: Option<String>,
    // children
    pub subject: Option<Subject>,
    pub body: Option<Body>,
    pub thread: Option<Thread>,
    #[cfg(feature = "xep_0203")]
    pub delay: Option<Delay>,
    #[cfg(feature = "xep_0131")]
    pub headers: Option<Headers>,
    #[cfg(feature = "xep_0172")]
    pub nick: Option<Nick>,
    #[cfg(feature = "xep_0060")]
    pub event: Option<Event>,
}

impl FromElement for Message {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("message")?;
        element.check_namespace(XMLNS)?;

        let from = element.attribute_opt("from")?;
        let id = element.attribute_opt("id")?;
        let to = element.attribute_opt("to")?;
        let r#type = element.attribute_opt("type")?.unwrap_or_default();
        let lang = element.attribute_opt_namespaced("lang", XML_NS)?;

        let subject = element.child_opt()?;
        let body = element.child_opt()?;
        let thread = element.child_opt()?;

        #[cfg(feature = "xep_0203")]
        let delay = element.child_opt()?;

        #[cfg(feature = "xep_0131")]
        let headers = element.child_opt()?;

        #[cfg(feature = "xep_0172")]
        let nick = element.child_opt()?;

        #[cfg(feature = "xep_0060")]
        let event = element.child_opt()?;

        Ok(Message {
            from,
            id,
            to,
            r#type,
            lang,
            subject,
            body,
            thread,
            #[cfg(feature = "xep_0203")]
            delay,
            #[cfg(feature = "xep_0131")]
            headers,
            #[cfg(feature = "xep_0172")]
            nick,
            #[cfg(feature = "xep_0060")]
            event,
        })
    }
}

impl IntoElement for Message {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        let builder = Element::builder("message", Some(XMLNS))
            .push_attribute_opt("from", self.from.clone())
            .push_attribute_opt("id", self.id.clone())
            .push_attribute_opt("to", self.to.clone())
            .push_attribute_opt("type", {
                if self.r#type == MessageType::Normal {
                    None
                } else {
                    Some(self.r#type)
                }
            })
            .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
            .push_child_opt(self.subject.clone())
            .push_child_opt(self.body.clone())
            .push_child_opt(self.thread.clone());

        #[cfg(feature = "xep_0203")]
        let builder = builder.push_child_opt(self.delay.clone());

        #[cfg(feature = "xep_0131")]
        let builder = builder.push_child_opt(self.headers.clone());

        #[cfg(feature = "xep_0172")]
        let builder = builder.push_child_opt(self.nick.clone());

        #[cfg(feature = "xep_0060")]
        let builder = builder.push_child_opt(self.event.clone());

        builder
    }
}

#[derive(Default, PartialEq, Eq, Copy, Clone, Debug)]
pub enum MessageType {
    Chat,
    Error,
    Groupchat,
    Headline,
    #[default]
    Normal,
}

impl FromStr for MessageType {
    type Err = DeserializeError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "chat" => Ok(MessageType::Chat),
            "error" => Ok(MessageType::Error),
            "groupchat" => Ok(MessageType::Groupchat),
            "headline" => Ok(MessageType::Headline),
            "normal" => Ok(MessageType::Normal),
            _ => Err(DeserializeError::FromStr(s.to_string())),
        }
    }
}

impl ToString for MessageType {
    fn to_string(&self) -> String {
        match self {
            MessageType::Chat => "chat".to_string(),
            MessageType::Error => "error".to_string(),
            MessageType::Groupchat => "groupchat".to_string(),
            MessageType::Headline => "headline".to_string(),
            MessageType::Normal => "normal".to_string(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Body {
    pub lang: Option<String>,
    // TODO: string stuff
    pub body: Option<String>,
}

impl FromElement for Body {
    fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("body")?;
        element.check_namespace(XMLNS)?;

        let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
        let body = element.pop_value_opt()?;

        Ok(Body { lang, body })
    }
}

impl IntoElement for Body {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("body", Some(XMLNS))
            .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
            .push_text_opt(self.body.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Subject {
    lang: Option<String>,
    subject: Option<String>,
}

impl FromElement for Subject {
    fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("subject")?;
        element.check_namespace(XMLNS)?;

        let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
        let subject = element.pop_value_opt()?;

        Ok(Subject { lang, subject })
    }
}

impl IntoElement for Subject {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("subject", Some(XMLNS))
            .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
            .push_text_opt(self.subject.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Thread {
    parent: Option<String>,
    thread: Option<String>,
}

impl FromElement for Thread {
    fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("thread")?;
        element.check_namespace(XMLNS)?;

        let parent = element.attribute_opt("parent")?;
        let thread = element.pop_value_opt()?;

        Ok(Thread { parent, thread })
    }
}

impl IntoElement for Thread {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("thread", Some(XMLNS))
            .push_attribute_opt("parent", self.parent.clone())
            .push_text_opt(self.thread.clone())
    }
}