implement remaining rfc6120 xml schemas
This commit is contained in:
parent
be198ca15b
commit
7c2577d196
|
@ -2,19 +2,16 @@ use std::str;
|
|||
use std::sync::Arc;
|
||||
|
||||
use async_recursion::async_recursion;
|
||||
use peanuts::element::{FromElement, IntoElement};
|
||||
use peanuts::element::IntoElement;
|
||||
use peanuts::{Reader, Writer};
|
||||
use rsasl::prelude::{Mechname, SASLClient, SASLConfig};
|
||||
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter, ReadHalf, WriteHalf};
|
||||
use tokio::time::timeout;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadHalf, WriteHalf};
|
||||
use tokio_native_tls::native_tls::TlsConnector;
|
||||
use tracing::{debug, info, instrument, trace};
|
||||
use trust_dns_resolver::proto::rr::domain::IntoLabel;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::connection::{Tls, Unencrypted};
|
||||
use crate::error::Error;
|
||||
use crate::stanza::bind::{Bind, BindType, FullJidType, ResourceType};
|
||||
use crate::stanza::client::error::Error as ClientError;
|
||||
use crate::stanza::client::iq::{Iq, IqType, Query};
|
||||
use crate::stanza::sasl::{Auth, Challenge, Mechanisms, Response, ServerResponse};
|
||||
use crate::stanza::starttls::{Proceed, StartTls};
|
||||
|
@ -257,7 +254,7 @@ where
|
|||
// server to client
|
||||
|
||||
// may or may not send a declaration
|
||||
let decl = self.reader.read_prolog().await?;
|
||||
let _decl = self.reader.read_prolog().await?;
|
||||
|
||||
// receive stream element and validate
|
||||
let text = str::from_utf8(self.reader.buffer.data()).unwrap();
|
||||
|
@ -471,7 +468,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn negotiate() {
|
||||
let jabber = Connection::connect_user("test@blos.sm", "slayed".to_string())
|
||||
let _jabber = Connection::connect_user("test@blos.sm", "slayed".to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.ensure_tls()
|
||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -9,14 +9,20 @@ pub mod jid;
|
|||
pub mod stanza;
|
||||
|
||||
pub use connection::Connection;
|
||||
use connection::Tls;
|
||||
pub use error::Error;
|
||||
pub use jabber::Jabber;
|
||||
pub use jid::JID;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub async fn login<J: TryInto<JID>, P: AsRef<str>>(jid: J, password: P) -> Result<Connection> {
|
||||
todo!()
|
||||
pub async fn login<J: AsRef<str>, P: AsRef<str>>(jid: J, password: P) -> Result<Jabber<Tls>> {
|
||||
Ok(Connection::connect_user(jid, password.as_ref().to_string())
|
||||
.await?
|
||||
.ensure_tls()
|
||||
.await?
|
||||
.negotiate()
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,8 +3,8 @@ use std::str::FromStr;
|
|||
use peanuts::element::{FromElement, IntoElement};
|
||||
use peanuts::{DeserializeError, Element};
|
||||
|
||||
use crate::stanza::error::Text;
|
||||
use crate::stanza::Error as StanzaError;
|
||||
use crate::stanza::stanza_error::Error as StanzaError;
|
||||
use crate::stanza::stanza_error::Text;
|
||||
|
||||
use super::XMLNS;
|
||||
|
||||
|
|
|
@ -1,37 +1,186 @@
|
|||
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>,
|
||||
r#type: Option<MessageType>,
|
||||
// can be omitted, if so default to normal
|
||||
r#type: MessageType,
|
||||
lang: Option<String>,
|
||||
// children
|
||||
subject: Option<Subject>,
|
||||
body: Option<Body>,
|
||||
thread: Option<Thread>,
|
||||
lang: Option<String>,
|
||||
}
|
||||
|
||||
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 {
|
||||
// TODO: NOT DONE
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,48 @@
|
|||
use iq::Iq;
|
||||
use message::Message;
|
||||
use peanuts::{
|
||||
element::{FromElement, 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),
|
||||
}
|
||||
|
||||
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 IntoElement for Stanza {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
match self {
|
||||
Stanza::Message(message) => message.builder(),
|
||||
Stanza::Presence(presence) => presence.builder(),
|
||||
Stanza::Iq(iq) => iq.builder(),
|
||||
Stanza::Error(error) => error.builder(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,77 @@
|
|||
use peanuts::element::{FromElement, IntoElement};
|
||||
use std::str::FromStr;
|
||||
|
||||
use peanuts::{
|
||||
element::{FromElement, IntoElement},
|
||||
DeserializeError, Element, XML_NS,
|
||||
};
|
||||
|
||||
use crate::JID;
|
||||
|
||||
use super::error::Error;
|
||||
use super::{error::Error, XMLNS};
|
||||
|
||||
pub struct Presence {
|
||||
from: Option<JID>,
|
||||
id: Option<String>,
|
||||
to: Option<JID>,
|
||||
r#type: PresenceType,
|
||||
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>,
|
||||
// ##other
|
||||
// content: Vec<Box<dyn AsElement>>,
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -29,6 +82,38 @@ pub enum PresenceType {
|
|||
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,
|
||||
|
@ -36,13 +121,106 @@ pub enum Show {
|
|||
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,
|
||||
}
|
||||
|
||||
// minLength 1 maxLength 1024
|
||||
pub struct String1024(String);
|
||||
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
|
||||
pub struct Priority(u8);
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,10 @@ use peanuts::declaration::VersionInfo;
|
|||
|
||||
pub mod bind;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod sasl;
|
||||
pub mod stanza_error;
|
||||
pub mod starttls;
|
||||
pub mod stream;
|
||||
pub mod stream_error;
|
||||
|
||||
pub static XML_VERSION: VersionInfo = VersionInfo::One;
|
||||
|
||||
pub use error::Error;
|
||||
|
|
|
@ -6,11 +6,12 @@ use peanuts::{element::Name, Element};
|
|||
use tracing::debug;
|
||||
|
||||
use crate::stanza::bind;
|
||||
use crate::{Error, JID};
|
||||
use crate::JID;
|
||||
|
||||
use super::client;
|
||||
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";
|
||||
|
||||
|
@ -162,3 +163,28 @@ impl FromElement for Feature {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
use peanuts::{
|
||||
element::{FromElement, IntoElement},
|
||||
DeserializeError, Element, XML_NS,
|
||||
};
|
||||
|
||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-streams";
|
||||
|
||||
#[derive(Clone)]
|
||||
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)]
|
||||
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())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue