// https://datatracker.ietf.org/doc/html/rfc6120#appendix-A.8 use peanuts::{ element::{FromElement, IntoElement}, Element, XML_NS, }; use thiserror::Error; pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; #[derive(Error, Clone, Debug)] pub enum Error { #[error("bad request")] BadRequest, #[error("conflict")] Conflict, #[error("feature not implemented")] FeatureNotImplemented, #[error("forbidden")] Forbidden, #[error("gone: {0:?}")] Gone(Option<String>), #[error("internal server error")] InternalServerError, #[error("item not found")] ItemNotFound, #[error("JID malformed")] JIDMalformed, #[error("not acceptable")] NotAcceptable, #[error("not allowed")] NotAllowed, #[error("not authorized")] NotAuthorized, #[error("policy violation")] PolicyViolation, #[error("recipient unavailable")] RecipientUnavailable, #[error("redirect: {0:?}")] Redirect(Option<String>), #[error("registration required")] RegistrationRequired, #[error("remote server not found")] RemoteServerNotFound, #[error("remote server timeout")] RemoteServerTimeout, #[error("resource constraint")] ResourceConstraint, #[error("service unavailable")] ServiceUnavailable, #[error("subscription required")] SubscriptionRequired, #[error("undefined condition")] UndefinedCondition, #[error("unexpected request")] 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>, pub 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()) } }