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())
}
}