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, pub id: Option, pub to: Option, // can be omitted, if so default to normal pub r#type: MessageType, pub lang: Option, // children pub subject: Option, pub body: Option, pub thread: Option, #[cfg(feature = "xep_0203")] pub delay: Option, #[cfg(feature = "xep_0131")] pub headers: Option, #[cfg(feature = "xep_0172")] pub nick: Option, #[cfg(feature = "xep_0060")] pub event: Option, } impl FromElement for Message { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { 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 { 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, // TODO: string stuff pub body: Option, } impl FromElement for Body { fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult { 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, subject: Option, } impl FromElement for Subject { fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult { 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, thread: Option, } impl FromElement for Thread { fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult { 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()) } }