diff options
Diffstat (limited to 'src/stanza')
-rw-r--r-- | src/stanza/bind.rs | 99 | ||||
-rw-r--r-- | src/stanza/client/error.rs | 83 | ||||
-rw-r--r-- | src/stanza/client/iq.rs | 124 | ||||
-rw-r--r-- | src/stanza/client/message.rs | 186 | ||||
-rw-r--r-- | src/stanza/client/mod.rs | 61 | ||||
-rw-r--r-- | src/stanza/client/presence.rs | 226 | ||||
-rw-r--r-- | src/stanza/mod.rs | 11 | ||||
-rw-r--r-- | src/stanza/sasl.rs | 246 | ||||
-rw-r--r-- | src/stanza/stanza_error.rs | 126 | ||||
-rw-r--r-- | src/stanza/starttls.rs | 94 | ||||
-rw-r--r-- | src/stanza/stream.rs | 191 | ||||
-rw-r--r-- | src/stanza/stream_error.rs | 137 |
12 files changed, 0 insertions, 1584 deletions
diff --git a/src/stanza/bind.rs b/src/stanza/bind.rs deleted file mode 100644 index 0e67a83..0000000 --- a/src/stanza/bind.rs +++ /dev/null @@ -1,99 +0,0 @@ -use peanuts::{ - element::{FromElement, IntoElement}, - Element, -}; - -use crate::JID; - -pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind"; - -#[derive(Clone)] -pub struct Bind { - pub r#type: Option<BindType>, -} - -impl FromElement for Bind { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("bind"); - element.check_name(XMLNS); - - let r#type = element.pop_child_opt()?; - - Ok(Bind { r#type }) - } -} - -impl IntoElement for Bind { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("bind", Some(XMLNS)).push_child_opt(self.r#type.clone()) - } -} - -#[derive(Clone)] -pub enum BindType { - Resource(ResourceType), - Jid(FullJidType), -} - -impl FromElement for BindType { - fn from_element(element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - match element.identify() { - (Some(XMLNS), "resource") => { - Ok(BindType::Resource(ResourceType::from_element(element)?)) - } - (Some(XMLNS), "jid") => Ok(BindType::Jid(FullJidType::from_element(element)?)), - _ => Err(peanuts::DeserializeError::UnexpectedElement(element)), - } - } -} - -impl IntoElement for BindType { - fn builder(&self) -> peanuts::element::ElementBuilder { - match self { - BindType::Resource(resource_type) => resource_type.builder(), - BindType::Jid(full_jid_type) => full_jid_type.builder(), - } - } -} - -// minLength 8 maxLength 3071 -#[derive(Clone)] -pub struct FullJidType(pub JID); - -impl FromElement for FullJidType { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("jid"); - element.check_namespace(XMLNS); - - let jid = element.pop_value()?; - - Ok(FullJidType(jid)) - } -} - -impl IntoElement for FullJidType { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("jid", Some(XMLNS)).push_text(self.0.clone()) - } -} - -// minLength 1 maxLength 1023 -#[derive(Clone)] -pub struct ResourceType(pub String); - -impl FromElement for ResourceType { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("resource")?; - element.check_namespace(XMLNS)?; - - let resource = element.pop_value()?; - - Ok(ResourceType(resource)) - } -} - -impl IntoElement for ResourceType { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("resource", Some(XMLNS)).push_text(self.0.clone()) - } -} diff --git a/src/stanza/client/error.rs b/src/stanza/client/error.rs deleted file mode 100644 index 545b9a7..0000000 --- a/src/stanza/client/error.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::str::FromStr; - -use peanuts::element::{FromElement, IntoElement}; -use peanuts::{DeserializeError, Element}; - -use crate::stanza::stanza_error::Error as StanzaError; -use crate::stanza::stanza_error::Text; - -use super::XMLNS; - -#[derive(Clone, Debug)] -pub struct Error { - by: Option<String>, - r#type: ErrorType, - // children (sequence) - error: StanzaError, - text: Option<Text>, -} - -impl FromElement for Error { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("error")?; - element.check_name(XMLNS)?; - - let by = element.attribute_opt("by")?; - let r#type = element.attribute("type")?; - let error = element.pop_child_one()?; - let text = element.pop_child_opt()?; - - Ok(Error { - by, - r#type, - error, - text, - }) - } -} - -impl IntoElement for Error { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("error", Some(XMLNS)) - .push_attribute_opt("by", self.by.clone()) - .push_attribute("type", self.r#type) - .push_child(self.error.clone()) - .push_child_opt(self.text.clone()) - } -} - -#[derive(Copy, Clone, Debug)] -pub enum ErrorType { - Auth, - Cancel, - Continue, - Modify, - Wait, -} - -impl FromStr for ErrorType { - type Err = DeserializeError; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "auth" => Ok(ErrorType::Auth), - "cancel" => Ok(ErrorType::Cancel), - "continue" => Ok(ErrorType::Continue), - "modify" => Ok(ErrorType::Modify), - "wait" => Ok(ErrorType::Wait), - _ => Err(DeserializeError::FromStr(s.to_string())), - } - } -} - -impl ToString for ErrorType { - fn to_string(&self) -> String { - match self { - ErrorType::Auth => "auth".to_string(), - ErrorType::Cancel => "cancel".to_string(), - ErrorType::Continue => "continue".to_string(), - ErrorType::Modify => "modify".to_string(), - ErrorType::Wait => "wait".to_string(), - } - } -} diff --git a/src/stanza/client/iq.rs b/src/stanza/client/iq.rs deleted file mode 100644 index b23f8b7..0000000 --- a/src/stanza/client/iq.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::str::FromStr; - -use peanuts::{ - element::{FromElement, IntoElement}, - DeserializeError, Element, XML_NS, -}; - -use crate::{ - stanza::{ - bind::{self, Bind}, - client::error::Error, - }, - JID, -}; - -use super::XMLNS; - -pub struct Iq { - pub from: Option<JID>, - pub id: String, - pub to: Option<JID>, - pub r#type: IqType, - pub lang: Option<String>, - // children - // ##other - pub query: Option<Query>, - pub errors: Vec<Error>, -} - -#[derive(Clone)] -pub enum Query { - Bind(Bind), - Unsupported, -} - -impl FromElement for Query { - fn from_element(element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - match element.identify() { - (Some(bind::XMLNS), "bind") => Ok(Query::Bind(Bind::from_element(element)?)), - _ => Ok(Query::Unsupported), - } - } -} - -impl IntoElement for Query { - fn builder(&self) -> peanuts::element::ElementBuilder { - match self { - Query::Bind(bind) => bind.builder(), - // TODO: consider what to do if attempt to serialize unsupported - Query::Unsupported => todo!(), - } - } -} - -impl FromElement for Iq { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("iq")?; - element.check_namespace(XMLNS)?; - - let from = element.attribute_opt("from")?; - let id = element.attribute("id")?; - let to = element.attribute_opt("to")?; - let r#type = element.attribute("type")?; - let lang = element.attribute_opt_namespaced("lang", XML_NS)?; - let query = element.pop_child_opt()?; - let errors = element.pop_children()?; - - Ok(Iq { - from, - id, - to, - r#type, - lang, - query, - errors, - }) - } -} - -impl IntoElement for Iq { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("iq", Some(XMLNS)) - .push_attribute_opt("from", self.from.clone()) - .push_attribute("id", self.id.clone()) - .push_attribute_opt("to", self.to.clone()) - .push_attribute("type", self.r#type) - .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone()) - .push_child_opt(self.query.clone()) - .push_children(self.errors.clone()) - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum IqType { - Error, - Get, - Result, - Set, -} - -impl FromStr for IqType { - type Err = DeserializeError; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "error" => Ok(IqType::Error), - "get" => Ok(IqType::Get), - "result" => Ok(IqType::Result), - "set" => Ok(IqType::Set), - _ => Err(DeserializeError::FromStr(s.to_string())), - } - } -} - -impl ToString for IqType { - fn to_string(&self) -> String { - match self { - IqType::Error => "error".to_string(), - IqType::Get => "get".to_string(), - IqType::Result => "result".to_string(), - IqType::Set => "set".to_string(), - } - } -} diff --git a/src/stanza/client/message.rs b/src/stanza/client/message.rs deleted file mode 100644 index 626d781..0000000 --- a/src/stanza/client/message.rs +++ /dev/null @@ -1,186 +0,0 @@ -use std::str::FromStr; - -use peanuts::{ - element::{FromElement, IntoElement}, - DeserializeError, Element, XML_NS, -}; - -use crate::JID; - -use super::XMLNS; - -pub struct Message { - from: Option<JID>, - id: Option<String>, - to: Option<JID>, - // can be omitted, if so default to normal - r#type: MessageType, - lang: Option<String>, - // children - subject: Option<Subject>, - body: Option<Body>, - thread: Option<Thread>, -} - -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()?; - - 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<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)] -pub struct Body { - lang: Option<String>, - 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)] -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)] -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()) - } -} diff --git a/src/stanza/client/mod.rs b/src/stanza/client/mod.rs deleted file mode 100644 index 2b063d6..0000000 --- a/src/stanza/client/mod.rs +++ /dev/null @@ -1,61 +0,0 @@ -use iq::Iq; -use message::Message; -use peanuts::{ - element::{Content, ContentBuilder, FromContent, FromElement, IntoContent, IntoElement}, - DeserializeError, -}; -use presence::Presence; - -use super::stream::{self, Error as StreamError}; - -pub mod error; -pub mod iq; -pub mod message; -pub mod presence; - -pub const XMLNS: &str = "jabber:client"; - -pub enum Stanza { - Message(Message), - Presence(Presence), - Iq(Iq), - Error(StreamError), - OtherContent(Content), -} - -impl FromContent for Stanza { - fn from_content(content: Content) -> peanuts::element::DeserializeResult<Self> { - match content { - Content::Element(element) => Ok(Stanza::from_element(element)?), - Content::Text(_) => Ok(Stanza::OtherContent(content)), - Content::PI => Ok(Stanza::OtherContent(content)), - Content::Comment(_) => Ok(Stanza::OtherContent(content)), - } - } -} - -impl FromElement for Stanza { - fn from_element(element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - match element.identify() { - (Some(XMLNS), "message") => Ok(Stanza::Message(Message::from_element(element)?)), - (Some(XMLNS), "presence") => Ok(Stanza::Presence(Presence::from_element(element)?)), - (Some(XMLNS), "iq") => Ok(Stanza::Iq(Iq::from_element(element)?)), - (Some(stream::XMLNS), "error") => { - Ok(Stanza::Error(StreamError::from_element(element)?)) - } - _ => Err(DeserializeError::UnexpectedElement(element)), - } - } -} - -impl IntoContent for Stanza { - fn builder(&self) -> peanuts::element::ContentBuilder { - match self { - Stanza::Message(message) => <Message as IntoContent>::builder(message), - Stanza::Presence(presence) => <Presence as IntoContent>::builder(presence), - Stanza::Iq(iq) => <Iq as IntoContent>::builder(iq), - Stanza::Error(error) => <StreamError as IntoContent>::builder(error), - Stanza::OtherContent(_content) => ContentBuilder::Comment("other-content".to_string()), - } - } -} diff --git a/src/stanza/client/presence.rs b/src/stanza/client/presence.rs deleted file mode 100644 index bcb04d4..0000000 --- a/src/stanza/client/presence.rs +++ /dev/null @@ -1,226 +0,0 @@ -use std::str::FromStr; - -use peanuts::{ - element::{FromElement, IntoElement}, - DeserializeError, Element, XML_NS, -}; - -use crate::JID; - -use super::{error::Error, XMLNS}; - -pub struct Presence { - from: Option<JID>, - id: Option<String>, - to: Option<JID>, - r#type: Option<PresenceType>, - lang: Option<String>, - // children - show: Option<Show>, - status: Option<Status>, - priority: Option<Priority>, - // TODO: ##other - // other: Vec<Other>, - errors: Vec<Error>, -} - -impl FromElement for Presence { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("presence")?; - 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")?; - let lang = element.attribute_opt_namespaced("lang", XML_NS)?; - - let show = element.child_opt()?; - let status = element.child_opt()?; - let priority = element.child_opt()?; - let errors = element.children()?; - - Ok(Presence { - from, - id, - to, - r#type, - lang, - show, - status, - priority, - errors, - }) - } -} - -impl IntoElement for Presence { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("presence", 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", self.r#type) - .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone()) - .push_child_opt(self.show) - .push_child_opt(self.status.clone()) - .push_child_opt(self.priority) - .push_children(self.errors.clone()) - } -} - -pub enum Other {} - -#[derive(Copy, Clone)] -pub enum PresenceType { - Error, - Probe, - Subscribe, - Subscribed, - Unavailable, - Unsubscribe, - Unsubscribed, -} - -impl FromStr for PresenceType { - type Err = DeserializeError; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "error" => Ok(PresenceType::Error), - "probe" => Ok(PresenceType::Probe), - "subscribe" => Ok(PresenceType::Subscribe), - "subscribed" => Ok(PresenceType::Subscribed), - "unavailable" => Ok(PresenceType::Unavailable), - "unsubscribe" => Ok(PresenceType::Unsubscribe), - "unsubscribed" => Ok(PresenceType::Unsubscribed), - s => Err(DeserializeError::FromStr(s.to_string())), - } - } -} - -impl ToString for PresenceType { - fn to_string(&self) -> String { - match self { - PresenceType::Error => "error".to_string(), - PresenceType::Probe => "probe".to_string(), - PresenceType::Subscribe => "subscribe".to_string(), - PresenceType::Subscribed => "subscribed".to_string(), - PresenceType::Unavailable => "unavailable".to_string(), - PresenceType::Unsubscribe => "unsubscribe".to_string(), - PresenceType::Unsubscribed => "unsubscribed".to_string(), - } - } -} - -#[derive(Copy, Clone)] -pub enum Show { - Away, - Chat, - Dnd, - Xa, -} - -impl FromElement for Show { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("show")?; - element.check_namespace(XMLNS)?; - - Ok(element.pop_value()?) - } -} - -impl FromStr for Show { - type Err = DeserializeError; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "away" => Ok(Show::Away), - "chat" => Ok(Show::Chat), - "dnd" => Ok(Show::Dnd), - "xa" => Ok(Show::Xa), - s => Err(DeserializeError::FromStr(s.to_string())), - } - } -} - -impl IntoElement for Show { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("show", Some(XMLNS)).push_text(*self) - } -} - -impl ToString for Show { - fn to_string(&self) -> String { - match self { - Show::Away => "away".to_string(), - Show::Chat => "chat".to_string(), - Show::Dnd => "dnd".to_string(), - Show::Xa => "xa".to_string(), - } - } -} - -#[derive(Clone)] -pub struct Status { - lang: Option<String>, - status: String1024, -} - -impl FromElement for Status { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("status")?; - element.check_namespace(XMLNS)?; - - let lang = element.attribute_opt_namespaced("lang", XML_NS)?; - let status = element.pop_value()?; - - Ok(Status { lang, status }) - } -} - -impl IntoElement for Status { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("status", Some(XMLNS)) - .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone()) - .push_text(self.status.clone()) - } -} - -// TODO: enforce? -/// minLength 1 maxLength 1024 -#[derive(Clone)] -pub struct String1024(pub String); - -impl FromStr for String1024 { - type Err = DeserializeError; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - Ok(String1024(s.to_string())) - } -} - -impl ToString for String1024 { - fn to_string(&self) -> String { - self.0.clone() - } -} - -// xs:byte -#[derive(Clone, Copy)] -pub struct Priority(pub i8); - -impl FromElement for Priority { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("priority")?; - element.check_namespace(XMLNS)?; - - Ok(Priority(element.pop_value()?)) - } -} - -impl IntoElement for Priority { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("priority", Some(XMLNS)).push_text(self.0) - } -} diff --git a/src/stanza/mod.rs b/src/stanza/mod.rs deleted file mode 100644 index 32716d3..0000000 --- a/src/stanza/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -use peanuts::declaration::VersionInfo; - -pub mod bind; -pub mod client; -pub mod sasl; -pub mod stanza_error; -pub mod starttls; -pub mod stream; -pub mod stream_error; - -pub static XML_VERSION: VersionInfo = VersionInfo::One; diff --git a/src/stanza/sasl.rs b/src/stanza/sasl.rs deleted file mode 100644 index ec6f63c..0000000 --- a/src/stanza/sasl.rs +++ /dev/null @@ -1,246 +0,0 @@ -use std::ops::Deref; - -use peanuts::{ - element::{FromElement, IntoElement}, - DeserializeError, Element, -}; -use tracing::debug; - -pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; - -#[derive(Debug, Clone)] -pub struct Mechanisms { - pub mechanisms: Vec<String>, -} - -impl FromElement for Mechanisms { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("mechanisms")?; - element.check_namespace(XMLNS)?; - debug!("getting mechanisms"); - let mechanisms: Vec<Mechanism> = element.pop_children()?; - debug!("gottting mechanisms"); - let mechanisms = mechanisms - .into_iter() - .map(|Mechanism(mechanism)| mechanism) - .collect(); - debug!("gottting mechanisms"); - - Ok(Mechanisms { mechanisms }) - } -} - -impl IntoElement for Mechanisms { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("mechanisms", Some(XMLNS)).push_children( - self.mechanisms - .iter() - .map(|mechanism| Mechanism(mechanism.to_string())) - .collect(), - ) - } -} - -pub struct Mechanism(String); - -impl FromElement for Mechanism { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("mechanism")?; - element.check_namespace(XMLNS)?; - - let mechanism = element.pop_value()?; - - Ok(Mechanism(mechanism)) - } -} - -impl IntoElement for Mechanism { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("mechanism", Some(XMLNS)).push_text(self.0.clone()) - } -} - -impl Deref for Mechanism { - type Target = str; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[derive(Debug)] -pub struct Auth { - pub mechanism: String, - pub sasl_data: String, -} - -impl IntoElement for Auth { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("auth", Some(XMLNS)) - .push_attribute("mechanism", self.mechanism.clone()) - .push_text(self.sasl_data.clone()) - } -} - -#[derive(Debug)] -pub struct Challenge(String); - -impl Deref for Challenge { - type Target = str; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl FromElement for Challenge { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("challenge")?; - element.check_namespace(XMLNS)?; - - let sasl_data = element.value()?; - - Ok(Challenge(sasl_data)) - } -} - -#[derive(Debug)] -pub struct Success(Option<String>); - -impl Deref for Success { - type Target = Option<String>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl FromElement for Success { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("success")?; - element.check_namespace(XMLNS)?; - - let sasl_data = element.value_opt()?; - - Ok(Success(sasl_data)) - } -} - -#[derive(Debug)] -pub enum ServerResponse { - Challenge(Challenge), - Success(Success), - Failure(Failure), -} - -impl FromElement for ServerResponse { - fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> { - debug!("identification: {:?}", element.identify()); - match element.identify() { - (Some(XMLNS), "challenge") => { - Ok(ServerResponse::Challenge(Challenge::from_element(element)?)) - } - (Some(XMLNS), "success") => { - Ok(ServerResponse::Success(Success::from_element(element)?)) - } - (Some(XMLNS), "failure") => { - Ok(ServerResponse::Failure(Failure::from_element(element)?)) - } - _ => Err(DeserializeError::UnexpectedElement(element)), - } - } -} - -#[derive(Debug)] -pub struct Response(String); - -impl Response { - pub fn new(response: String) -> Self { - Self(response) - } -} - -impl Deref for Response { - type Target = str; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl IntoElement for Response { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("response", Some(XMLNS)).push_text(self.0.clone()) - } -} - -#[derive(Debug)] -pub struct Failure { - r#type: Option<FailureType>, - text: Option<Text>, -} - -impl FromElement for Failure { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("failure")?; - element.check_namespace(XMLNS)?; - - let r#type = element.pop_child_opt()?; - let text = element.pop_child_opt()?; - - Ok(Failure { r#type, text }) - } -} - -#[derive(Debug)] -pub enum FailureType { - Aborted, - AccountDisabled, - CredentialsExpired, - EncryptionRequired, - IncorrectEncoding, - InvalidAuthzid, - InvalidMechanism, - MalformedRequest, - MechanismTooWeak, - NotAuthorized, - TemporaryAuthFailure, -} - -impl FromElement for FailureType { - fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> { - match element.identify() { - (Some(XMLNS), "aborted") => Ok(FailureType::Aborted), - (Some(XMLNS), "account-disabled") => Ok(FailureType::AccountDisabled), - (Some(XMLNS), "credentials-expired") => Ok(FailureType::CredentialsExpired), - (Some(XMLNS), "encryption-required") => Ok(FailureType::EncryptionRequired), - (Some(XMLNS), "incorrect-encoding") => Ok(FailureType::IncorrectEncoding), - (Some(XMLNS), "invalid-authzid") => Ok(FailureType::InvalidAuthzid), - (Some(XMLNS), "invalid-mechanism") => Ok(FailureType::InvalidMechanism), - (Some(XMLNS), "malformed-request") => Ok(FailureType::MalformedRequest), - (Some(XMLNS), "mechanism-too-weak") => Ok(FailureType::MechanismTooWeak), - (Some(XMLNS), "not-authorized") => Ok(FailureType::NotAuthorized), - (Some(XMLNS), "temporary-auth-failure") => Ok(FailureType::TemporaryAuthFailure), - _ => Err(DeserializeError::UnexpectedElement(element)), - } - } -} - -#[derive(Debug)] -pub struct Text { - lang: Option<String>, - text: Option<String>, -} - -impl FromElement for Text { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("text")?; - element.check_namespace(XMLNS)?; - - let lang = element.attribute_opt_namespaced("lang", peanuts::XML_NS)?; - - let text = element.pop_value_opt()?; - - Ok(Text { lang, text }) - } -} diff --git a/src/stanza/stanza_error.rs b/src/stanza/stanza_error.rs deleted file mode 100644 index 99c1f15..0000000 --- a/src/stanza/stanza_error.rs +++ /dev/null @@ -1,126 +0,0 @@ -// https://datatracker.ietf.org/doc/html/rfc6120#appendix-A.8 - -use peanuts::{ - element::{FromElement, IntoElement}, - Element, XML_NS, -}; - -pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; - -#[derive(Clone, Debug)] -pub enum Error { - BadRequest, - Conflict, - FeatureNotImplemented, - Forbidden, - Gone(Option<String>), - InternalServerError, - ItemNotFound, - JidMalformed, - NotAcceptable, - NotAllowed, - NotAuthorized, - PolicyViolation, - RecipientUnavailable, - Redirect(Option<String>), - RegistrationRequired, - RemoteServerNotFound, - RemoteServerTimeout, - ResourceConstraint, - ServiceUnavailable, - SubscriptionRequired, - UndefinedCondition, - UnexpectedRequest, -} - -impl FromElement for Error { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - let error; - match element.identify() { - (Some(XMLNS), "bad-request") => error = Error::BadRequest, - (Some(XMLNS), "conflict") => error = Error::Conflict, - (Some(XMLNS), "feature-not-implemented") => error = Error::FeatureNotImplemented, - (Some(XMLNS), "forbidden") => error = Error::Forbidden, - (Some(XMLNS), "gone") => return Ok(Error::Gone(element.pop_value_opt()?)), - (Some(XMLNS), "internal-server-error") => error = Error::InternalServerError, - (Some(XMLNS), "item-not-found") => error = Error::ItemNotFound, - (Some(XMLNS), "jid-malformed") => error = Error::JidMalformed, - (Some(XMLNS), "not-acceptable") => error = Error::NotAcceptable, - (Some(XMLNS), "not-allowed") => error = Error::NotAllowed, - (Some(XMLNS), "not-authorized") => error = Error::NotAuthorized, - (Some(XMLNS), "policy-violation") => error = Error::PolicyViolation, - (Some(XMLNS), "recipient-unavailable") => error = Error::RecipientUnavailable, - (Some(XMLNS), "redirect") => return Ok(Error::Redirect(element.pop_value_opt()?)), - (Some(XMLNS), "registration-required") => error = Error::RegistrationRequired, - (Some(XMLNS), "remote-server-not-found") => error = Error::RemoteServerNotFound, - (Some(XMLNS), "remote-server-timeout") => error = Error::RemoteServerTimeout, - (Some(XMLNS), "resource-constraint") => error = Error::ResourceConstraint, - (Some(XMLNS), "service-unavailable") => error = Error::ServiceUnavailable, - (Some(XMLNS), "subscription-required") => error = Error::SubscriptionRequired, - (Some(XMLNS), "undefined-condition") => error = Error::UndefinedCondition, - (Some(XMLNS), "unexpected-request") => error = Error::UnexpectedRequest, - _ => return Err(peanuts::DeserializeError::UnexpectedElement(element)), - } - element.no_more_content()?; - return Ok(error); - } -} - -impl IntoElement for Error { - fn builder(&self) -> peanuts::element::ElementBuilder { - match self { - Error::BadRequest => Element::builder("bad-request", Some(XMLNS)), - Error::Conflict => Element::builder("conflict", Some(XMLNS)), - Error::FeatureNotImplemented => { - Element::builder("feature-not-implemented", Some(XMLNS)) - } - Error::Forbidden => Element::builder("forbidden", Some(XMLNS)), - Error::Gone(r) => Element::builder("gone", Some(XMLNS)).push_text_opt(r.clone()), - Error::InternalServerError => Element::builder("internal-server-error", Some(XMLNS)), - Error::ItemNotFound => Element::builder("item-not-found", Some(XMLNS)), - Error::JidMalformed => Element::builder("jid-malformed", Some(XMLNS)), - Error::NotAcceptable => Element::builder("not-acceptable", Some(XMLNS)), - Error::NotAllowed => Element::builder("not-allowed", Some(XMLNS)), - Error::NotAuthorized => Element::builder("not-authorized", Some(XMLNS)), - Error::PolicyViolation => Element::builder("policy-violation", Some(XMLNS)), - Error::RecipientUnavailable => Element::builder("recipient-unavailable", Some(XMLNS)), - Error::Redirect(r) => { - Element::builder("redirect", Some(XMLNS)).push_text_opt(r.clone()) - } - Error::RegistrationRequired => Element::builder("registration-required", Some(XMLNS)), - Error::RemoteServerNotFound => Element::builder("remote-server-not-found", Some(XMLNS)), - Error::RemoteServerTimeout => Element::builder("remote-server-timeout", Some(XMLNS)), - Error::ResourceConstraint => Element::builder("resource-constraint", Some(XMLNS)), - Error::ServiceUnavailable => Element::builder("service-unavailable", Some(XMLNS)), - Error::SubscriptionRequired => Element::builder("subscription-required", Some(XMLNS)), - Error::UndefinedCondition => Element::builder("undefined-condition", Some(XMLNS)), - Error::UnexpectedRequest => Element::builder("unexpected-request", Some(XMLNS)), - } - } -} - -#[derive(Clone, Debug)] -pub struct Text { - lang: Option<String>, - text: Option<String>, -} - -impl FromElement for Text { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("text")?; - element.check_name(XMLNS)?; - - let lang = element.attribute_opt_namespaced("lang", XML_NS)?; - let text = element.pop_value_opt()?; - - Ok(Text { lang, text }) - } -} - -impl IntoElement for Text { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("text", Some(XMLNS)) - .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone()) - .push_text_opt(self.text.clone()) - } -} diff --git a/src/stanza/starttls.rs b/src/stanza/starttls.rs deleted file mode 100644 index fb66711..0000000 --- a/src/stanza/starttls.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use peanuts::{ - element::{Content, FromElement, IntoElement, Name, NamespaceDeclaration}, - Element, -}; - -pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; - -#[derive(Debug, Clone)] -pub struct StartTls { - pub required: bool, -} - -impl IntoElement for StartTls { - fn builder(&self) -> peanuts::element::ElementBuilder { - let mut builder = Element::builder("starttls", Some(XMLNS)); - - if self.required { - builder = builder.push_child(Required) - } - - builder - } -} - -impl FromElement for StartTls { - fn from_element( - mut element: peanuts::Element, - ) -> std::result::Result<StartTls, peanuts::DeserializeError> { - element.check_name("starttls")?; - element.check_namespace(XMLNS)?; - - let mut required = false; - if let Some(_) = element.child_opt::<Required>()? { - required = true; - } - - Ok(StartTls { required }) - } -} - -#[derive(Debug)] -pub struct Required; - -impl FromElement for Required { - fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("required")?; - element.check_namespace(XMLNS)?; - - Ok(Required) - } -} - -impl IntoElement for Required { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("required", Some(XMLNS)) - } -} - -#[derive(Debug)] -pub struct Proceed; - -impl IntoElement for Proceed { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("proceed", Some(XMLNS)) - } -} - -impl FromElement for Proceed { - fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("proceed")?; - element.check_namespace(XMLNS)?; - - Ok(Proceed) - } -} - -pub struct Failure; - -impl IntoElement for Failure { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("failure", Some(XMLNS)) - } -} - -impl FromElement for Failure { - fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("failure")?; - element.check_namespace(XMLNS)?; - - Ok(Failure) - } -} diff --git a/src/stanza/stream.rs b/src/stanza/stream.rs deleted file mode 100644 index 84d62d9..0000000 --- a/src/stanza/stream.rs +++ /dev/null @@ -1,191 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use peanuts::element::{Content, ElementBuilder, FromElement, IntoElement, NamespaceDeclaration}; -use peanuts::XML_NS; -use peanuts::{element::Name, Element}; -use tracing::debug; - -use crate::stanza::bind; -use crate::JID; - -use super::sasl::{self, Mechanisms}; -use super::starttls::{self, StartTls}; -use super::stream_error::{Error as StreamError, Text}; -use super::{client, stream_error}; - -pub const XMLNS: &str = "http://etherx.jabber.org/streams"; - -// MUST be qualified by stream namespace -// #[derive(XmlSerialize, XmlDeserialize)] -// #[peanuts(xmlns = XMLNS)] -#[derive(Debug)] -pub struct Stream { - pub from: Option<JID>, - to: Option<JID>, - id: Option<String>, - version: Option<String>, - // TODO: lang enum - lang: Option<String>, - // #[peanuts(content)] - // content: Message, -} - -impl FromElement for Stream { - fn from_element(mut element: Element) -> std::result::Result<Self, peanuts::DeserializeError> { - element.check_namespace(XMLNS)?; - element.check_name("stream")?; - - let from = element.attribute_opt("from")?; - let to = element.attribute_opt("to")?; - let id = element.attribute_opt("id")?; - let version = element.attribute_opt("version")?; - let lang = element.attribute_opt_namespaced("lang", peanuts::XML_NS)?; - - Ok(Stream { - from, - to, - id, - version, - lang, - }) - } -} - -impl IntoElement for Stream { - fn builder(&self) -> ElementBuilder { - Element::builder("stream", Some(XMLNS.to_string())) - .push_namespace_declaration_override(Some("stream"), XMLNS) - .push_namespace_declaration_override(None::<&str>, client::XMLNS) - .push_attribute_opt("to", self.to.clone()) - .push_attribute_opt("from", self.from.clone()) - .push_attribute_opt("id", self.id.clone()) - .push_attribute_opt("version", self.version.clone()) - .push_attribute_opt_namespaced(peanuts::XML_NS, "to", self.lang.clone()) - } -} - -impl<'s> Stream { - pub fn new( - from: Option<JID>, - to: Option<JID>, - id: Option<String>, - version: Option<String>, - lang: Option<String>, - ) -> Self { - Self { - from, - to, - id, - version, - lang, - } - } - - /// For initial stream headers, the initiating entity SHOULD include the 'xml:lang' attribute. - /// For privacy, it is better to not set `from` when sending a client stanza over an unencrypted connection. - pub fn new_client(from: Option<JID>, to: JID, id: Option<String>, lang: String) -> Self { - Self { - from, - to: Some(to), - id, - version: Some("1.0".to_string()), - lang: Some(lang), - } - } -} - -#[derive(Debug)] -pub struct Features { - pub features: Vec<Feature>, -} - -impl IntoElement for Features { - fn builder(&self) -> ElementBuilder { - Element::builder("features", Some(XMLNS)).push_children(self.features.clone()) - } -} - -impl FromElement for Features { - fn from_element( - mut element: Element, - ) -> std::result::Result<Features, peanuts::DeserializeError> { - element.check_namespace(XMLNS)?; - element.check_name("features")?; - - debug!("got features stanza"); - let features = element.children()?; - debug!("got features period"); - - Ok(Features { features }) - } -} - -#[derive(Debug, Clone)] -pub enum Feature { - StartTls(StartTls), - Sasl(Mechanisms), - Bind, - Unknown, -} - -impl IntoElement for Feature { - fn builder(&self) -> ElementBuilder { - match self { - Feature::StartTls(start_tls) => start_tls.builder(), - Feature::Sasl(mechanisms) => mechanisms.builder(), - Feature::Bind => todo!(), - Feature::Unknown => todo!(), - } - } -} - -impl FromElement for Feature { - fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> { - let identity = element.identify(); - debug!("identity: {:?}", identity); - match element.identify() { - (Some(starttls::XMLNS), "starttls") => { - debug!("identified starttls"); - Ok(Feature::StartTls(StartTls::from_element(element)?)) - } - (Some(sasl::XMLNS), "mechanisms") => { - debug!("identified mechanisms"); - Ok(Feature::Sasl(Mechanisms::from_element(element)?)) - } - (Some(bind::XMLNS), "bind") => { - debug!("identified bind"); - Ok(Feature::Bind) - } - _ => { - debug!("identified unknown feature"); - Ok(Feature::Unknown) - } - } - } -} - -#[derive(Debug)] -pub struct Error { - error: StreamError, - text: Option<Text>, -} - -impl FromElement for Error { - fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("error")?; - element.check_namespace(XMLNS)?; - - let error = element.pop_child_one()?; - let text = element.pop_child_opt()?; - - Ok(Error { error, text }) - } -} - -impl IntoElement for Error { - fn builder(&self) -> ElementBuilder { - Element::builder("error", Some(XMLNS)) - .push_child(self.error.clone()) - .push_child_opt(self.text.clone()) - } -} diff --git a/src/stanza/stream_error.rs b/src/stanza/stream_error.rs deleted file mode 100644 index 5ae04a6..0000000 --- a/src/stanza/stream_error.rs +++ /dev/null @@ -1,137 +0,0 @@ -use peanuts::{ - element::{FromElement, IntoElement}, - DeserializeError, Element, XML_NS, -}; - -pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-streams"; - -#[derive(Clone, Debug)] -pub enum Error { - BadFormat, - BadNamespacePrefix, - Conflict, - ConnectionTimeout, - HostGone, - HostUnknown, - ImproperAddressing, - InternalServerError, - InvalidFrom, - InvalidId, - InvalidNamespace, - InvalidXml, - NotAuthorized, - NotWellFormed, - PolicyViolation, - RemoteConnectionFailed, - Reset, - ResourceConstraint, - RestrictedXml, - SeeOtherHost(Option<String>), - SystemShutdown, - UndefinedCondition, - UnsupportedEncoding, - UnsupportedStanzaType, - UnsupportedVersion, -} - -impl FromElement for Error { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - let error; - match element.identify() { - (Some(XMLNS), "bad-format") => error = Error::BadFormat, - (Some(XMLNS), "bad-namespace-prefix") => error = Error::BadNamespacePrefix, - (Some(XMLNS), "conflict") => error = Error::Conflict, - (Some(XMLNS), "connection-timeout") => error = Error::ConnectionTimeout, - (Some(XMLNS), "host-gone") => error = Error::HostGone, - (Some(XMLNS), "host-unknown") => error = Error::HostUnknown, - (Some(XMLNS), "improper-addressing") => error = Error::ImproperAddressing, - (Some(XMLNS), "internal-server-error") => error = Error::InternalServerError, - (Some(XMLNS), "invalid-from") => error = Error::InvalidFrom, - (Some(XMLNS), "invalid-id") => error = Error::InvalidId, - (Some(XMLNS), "invalid-namespace") => error = Error::InvalidNamespace, - (Some(XMLNS), "invalid-xml") => error = Error::InvalidXml, - (Some(XMLNS), "not-authorized") => error = Error::NotAuthorized, - (Some(XMLNS), "not-well-formed") => error = Error::NotWellFormed, - (Some(XMLNS), "policy-violation") => error = Error::PolicyViolation, - (Some(XMLNS), "remote-connection-failed") => error = Error::RemoteConnectionFailed, - (Some(XMLNS), "reset") => error = Error::Reset, - (Some(XMLNS), "resource-constraint") => error = Error::ResourceConstraint, - (Some(XMLNS), "restricted-xml") => error = Error::RestrictedXml, - (Some(XMLNS), "see-other-host") => { - return Ok(Error::SeeOtherHost(element.pop_value_opt()?)) - } - (Some(XMLNS), "system-shutdown") => error = Error::SystemShutdown, - (Some(XMLNS), "undefined-condition") => error = Error::UndefinedCondition, - (Some(XMLNS), "unsupported-encoding") => error = Error::UnsupportedEncoding, - (Some(XMLNS), "unsupported-stanza-type") => error = Error::UnsupportedStanzaType, - (Some(XMLNS), "unsupported-version") => error = Error::UnsupportedVersion, - _ => return Err(DeserializeError::UnexpectedElement(element)), - } - element.no_more_content()?; - return Ok(error); - } -} - -impl IntoElement for Error { - fn builder(&self) -> peanuts::element::ElementBuilder { - match self { - Error::BadFormat => Element::builder("bad-format", Some(XMLNS)), - Error::BadNamespacePrefix => Element::builder("bad-namespace-prefix", Some(XMLNS)), - Error::Conflict => Element::builder("conflict", Some(XMLNS)), - Error::ConnectionTimeout => Element::builder("connection-timeout", Some(XMLNS)), - Error::HostGone => Element::builder("host-gone", Some(XMLNS)), - Error::HostUnknown => Element::builder("host-unknown", Some(XMLNS)), - Error::ImproperAddressing => Element::builder("improper-addressing", Some(XMLNS)), - Error::InternalServerError => Element::builder("internal-server-error", Some(XMLNS)), - Error::InvalidFrom => Element::builder("invalid-from", Some(XMLNS)), - Error::InvalidId => Element::builder("invalid-id", Some(XMLNS)), - Error::InvalidNamespace => Element::builder("invalid-namespace", Some(XMLNS)), - Error::InvalidXml => Element::builder("invalid-xml", Some(XMLNS)), - Error::NotAuthorized => Element::builder("not-authorized", Some(XMLNS)), - Error::NotWellFormed => Element::builder("not-well-formed", Some(XMLNS)), - Error::PolicyViolation => Element::builder("policy-violation", Some(XMLNS)), - Error::RemoteConnectionFailed => { - Element::builder("remote-connection-failed", Some(XMLNS)) - } - Error::Reset => Element::builder("reset", Some(XMLNS)), - Error::ResourceConstraint => Element::builder("resource-constraint", Some(XMLNS)), - Error::RestrictedXml => Element::builder("restricted-xml", Some(XMLNS)), - Error::SeeOtherHost(h) => { - Element::builder("see-other-host", Some(XMLNS)).push_text_opt(h.clone()) - } - Error::SystemShutdown => Element::builder("system-shutdown", Some(XMLNS)), - Error::UndefinedCondition => Element::builder("undefined-condition", Some(XMLNS)), - Error::UnsupportedEncoding => Element::builder("unsupported-encoding", Some(XMLNS)), - Error::UnsupportedStanzaType => { - Element::builder("unsupported-stanza-type", Some(XMLNS)) - } - Error::UnsupportedVersion => Element::builder("unsupported-version", Some(XMLNS)), - } - } -} - -#[derive(Clone, Debug)] -pub struct Text { - text: Option<String>, - lang: Option<String>, -} - -impl FromElement for Text { - fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { - element.check_name("text")?; - element.check_name(XMLNS)?; - - let lang = element.attribute_opt_namespaced("lang", XML_NS)?; - let text = element.pop_value_opt()?; - - Ok(Text { lang, text }) - } -} - -impl IntoElement for Text { - fn builder(&self) -> peanuts::element::ElementBuilder { - Element::builder("text", Some(XMLNS)) - .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone()) - .push_text_opt(self.text.clone()) - } -} |