From 1b91ff690488b65b552c90bd5392b9a300c8c981 Mon Sep 17 00:00:00 2001 From: cel 🌸 Date: Wed, 4 Dec 2024 18:18:37 +0000 Subject: use cargo workspace --- stanza/src/client/message.rs | 185 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 stanza/src/client/message.rs (limited to 'stanza/src/client/message.rs') diff --git a/stanza/src/client/message.rs b/stanza/src/client/message.rs new file mode 100644 index 0000000..b9d995f --- /dev/null +++ b/stanza/src/client/message.rs @@ -0,0 +1,185 @@ +use std::str::FromStr; + +use jid::JID; +use peanuts::{ + element::{FromElement, IntoElement}, + DeserializeError, Element, XML_NS, +}; + +use super::XMLNS; + +pub struct Message { + from: Option, + id: Option, + to: Option, + // can be omitted, if so default to normal + r#type: MessageType, + lang: Option, + // children + subject: Option, + body: Option, + thread: 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()?; + + Ok(Message { + from, + id, + to, + r#type, + lang, + subject, + body, + thread, + }) + } +} + +impl IntoElement for Message { + fn builder(&self) -> peanuts::element::ElementBuilder { + 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()) + } +} + +#[derive(Default, PartialEq, Eq, Copy, Clone)] +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)] +pub struct Body { + lang: Option, + 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)] +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)] +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()) + } +} -- cgit