use std::{fmt::Display, str::FromStr}; use peanuts::{ element::{FromElement, IntoElement}, DeserializeError, Element, }; use thiserror::Error; pub const XMLNS: &str = "http://jabber.org/protocol/pubsub#errors"; #[derive(Error, Clone, Debug)] pub enum Error { #[error("closed node")] ClosedNode, #[error("configuration required")] ConfigurationRequired, #[error("invalid jid")] InvalidJID, #[error("invalid options")] InvalidOptions, #[error("invalid payload")] InvalidPayload, #[error("invalid subscription id")] InvalidSubID, #[error("item forbidden")] ItemForbidden, #[error("item required")] ItemRequired, #[error("jid required")] JIDRequired, #[error("max items exceeded")] MaxItemsExceeded, #[error("max nodes exceeded")] MaxNodesExceeded, #[error("node id required")] NodeIDRequired, #[error("not in roster group")] NotInRosterGroup, #[error("not subscribed")] NotSubscribed, #[error("payload too big")] PayloadTooBig, #[error("payload required")] PayloadRequired, #[error("pending subscription")] PendingSubscription, #[error("precondition not met")] PreconditionNotMet, #[error("presence subscription required")] PresenceSubscriptionRequired, #[error("subscription id required")] SubIDRequired, #[error("too many subscriptions")] TooManySubscriptions, #[error("unsupported feature: {0}")] Unsupported(Feature), #[error("unsupported access mode")] UnsupportedAccessModel, } impl FromElement for Error { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { match element.identify() { (Some(XMLNS), "closed-node") => Ok(Self::ClosedNode), (Some(XMLNS), "configuration-required") => Ok(Self::ConfigurationRequired), (Some(XMLNS), "invalid-jid") => Ok(Self::InvalidJID), (Some(XMLNS), "invalid-options") => Ok(Self::InvalidOptions), (Some(XMLNS), "invalid-payload") => Ok(Self::InvalidPayload), (Some(XMLNS), "invalid-subid") => Ok(Self::InvalidSubID), (Some(XMLNS), "item-forbidden") => Ok(Self::ItemForbidden), (Some(XMLNS), "item-required") => Ok(Self::ItemRequired), (Some(XMLNS), "jid-required") => Ok(Self::JIDRequired), (Some(XMLNS), "max-items-exceeded") => Ok(Self::MaxItemsExceeded), (Some(XMLNS), "max-nodes-exceeded") => Ok(Self::MaxNodesExceeded), (Some(XMLNS), "nodeid-required") => Ok(Self::NodeIDRequired), (Some(XMLNS), "not-in-roster-group") => Ok(Self::NotInRosterGroup), (Some(XMLNS), "not-subscribed") => Ok(Self::NotSubscribed), (Some(XMLNS), "payload-too-big") => Ok(Self::PayloadTooBig), (Some(XMLNS), "payload-required") => Ok(Self::PayloadRequired), (Some(XMLNS), "pending-subscription") => Ok(Self::PendingSubscription), (Some(XMLNS), "precondition-not-met") => Ok(Self::PreconditionNotMet), (Some(XMLNS), "presence-subscription-required") => { Ok(Self::PresenceSubscriptionRequired) } (Some(XMLNS), "subid-required") => Ok(Self::SubIDRequired), (Some(XMLNS), "too-many-subscription") => Ok(Self::TooManySubscriptions), (Some(XMLNS), "unsupported") => Ok(Self::Unsupported(element.attribute("feature")?)), (Some(XMLNS), "unsupported-access-model") => Ok(Self::UnsupportedAccessModel), _ => return Err(peanuts::DeserializeError::UnexpectedElement(element)), } } } impl IntoElement for Error { fn builder(&self) -> peanuts::element::ElementBuilder { match self { Error::ClosedNode => Element::builder("closed-node", Some(XMLNS)), Error::ConfigurationRequired => Element::builder("configuration-required", Some(XMLNS)), Error::InvalidJID => Element::builder("invalid-jid", Some(XMLNS)), Error::InvalidOptions => Element::builder("invalid-options", Some(XMLNS)), Error::InvalidPayload => Element::builder("invalid-payload", Some(XMLNS)), Error::InvalidSubID => Element::builder("invalid-subid", Some(XMLNS)), Error::ItemForbidden => Element::builder("item-forbidden", Some(XMLNS)), Error::ItemRequired => Element::builder("item-required", Some(XMLNS)), Error::JIDRequired => Element::builder("jid-required", Some(XMLNS)), Error::MaxItemsExceeded => Element::builder("max-items-exceeded", Some(XMLNS)), Error::MaxNodesExceeded => Element::builder("max-nodes-exceeded", Some(XMLNS)), Error::NodeIDRequired => Element::builder("node-id-required", Some(XMLNS)), Error::NotInRosterGroup => Element::builder("not-in-roster-group", Some(XMLNS)), Error::NotSubscribed => Element::builder("not-subscribed", Some(XMLNS)), Error::PayloadTooBig => Element::builder("payload-too-big", Some(XMLNS)), Error::PayloadRequired => Element::builder("payload-required", Some(XMLNS)), Error::PendingSubscription => Element::builder("pending-subscription", Some(XMLNS)), Error::PreconditionNotMet => Element::builder("precondition-not-met", Some(XMLNS)), Error::PresenceSubscriptionRequired => { Element::builder("presence-subscription-required", Some(XMLNS)) } Error::SubIDRequired => Element::builder("subid-required", Some(XMLNS)), Error::TooManySubscriptions => Element::builder("too-many-subscriptions", Some(XMLNS)), Error::Unsupported(feature) => { Element::builder("unsupported", Some(XMLNS)).push_attribute("feature", feature) } Error::UnsupportedAccessModel => { Element::builder("unsupported-access-model", Some(XMLNS)) } } } } #[derive(Debug, Clone)] pub enum Feature { AccessAuthorize, AccessOpen, AccessPresence, AccessRoster, AccessWhitelist, AutoCreate, AutoSubscribe, Collections, ConfigNode, CreateAndConfigure, CreateNodes, DeleteItems, DeleteNodes, FilteredNotifications, GetPending, InstantNodes, ItemIDs, LastPublished, LeasedSubscription, ManageSubscriptions, MemberAffiliation, MetaData, ModifyAffiliations, MultiCollection, MultiItems, MultiSubscribe, OutcastAffiliation, PersistentItems, PresenceNotifications, PresenceSubscribe, Publish, PublishOptions, PublishOnlyAffiliation, PublisherAffiliation, PurgeNodes, RetractItems, RetrieveAffiliations, RetrieveDefault, RetrieveDefaultSub, RetrieveItems, RetrieveSubscriptions, Subscribe, SubscriptionOptions, SubscriptionNotifications, } impl Display for Feature { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let str = match self { Feature::AccessAuthorize => "access-authorize", Feature::AccessOpen => "access-open", Feature::AccessPresence => "access-presence", Feature::AccessRoster => "access-roster", Feature::AccessWhitelist => "access-whitelist", Feature::AutoCreate => "auto-create", Feature::AutoSubscribe => "auto-subscribe", Feature::Collections => "collections", Feature::ConfigNode => "config-node", Feature::CreateAndConfigure => "create-and-configure", Feature::CreateNodes => "create-nodes", Feature::DeleteItems => "delete-items", Feature::DeleteNodes => "delete-nodes", Feature::FilteredNotifications => "filtered-notifications", Feature::GetPending => "get-pending", Feature::InstantNodes => "instant-nodes", Feature::ItemIDs => "item-ids", Feature::LastPublished => "last-published", Feature::LeasedSubscription => "leased-subscription", Feature::ManageSubscriptions => "manage-subscriptions", Feature::MemberAffiliation => "member-affiliation", Feature::MetaData => "meta-data", Feature::ModifyAffiliations => "modify-affiliations", Feature::MultiCollection => "multi-collection", Feature::MultiItems => "multi-items", Feature::MultiSubscribe => "multi-subscribe", Feature::OutcastAffiliation => "outcast-affiliation", Feature::PersistentItems => "persistent-items", Feature::PresenceNotifications => "presence-notifications", Feature::PresenceSubscribe => "presence-subscribe", Feature::Publish => "publish", Feature::PublishOptions => "publish-options", Feature::PublishOnlyAffiliation => "publish-only-affiliation", Feature::PublisherAffiliation => "publisher-affiliation", Feature::PurgeNodes => "purge-nodes", Feature::RetractItems => "retract-items", Feature::RetrieveAffiliations => "retrieve-affiliations", Feature::RetrieveDefault => "retrieve-default", Feature::RetrieveDefaultSub => "retrieve-default-sub", Feature::RetrieveItems => "retrieve-items", Feature::RetrieveSubscriptions => "retrieve-subscriptions", Feature::Subscribe => "subscribe", Feature::SubscriptionOptions => "subscription-options", Feature::SubscriptionNotifications => "subscription-notifications", }; f.write_str(str) } } impl FromStr for Feature { type Err = DeserializeError; fn from_str(s: &str) -> Result { Ok(match s { "access-authorize" => Feature::AccessAuthorize, "access-open" => Feature::AccessOpen, "access-presence" => Feature::AccessPresence, "access-roster" => Feature::AccessRoster, "access-whitelist" => Feature::AccessWhitelist, "auto-create" => Feature::AutoCreate, "auto-subscribe" => Feature::AutoSubscribe, "collections" => Feature::Collections, "config-node" => Feature::ConfigNode, "create-and-configure" => Feature::CreateAndConfigure, "create-nodes" => Feature::CreateNodes, "delete-items" => Feature::DeleteItems, "delete-nodes" => Feature::DeleteNodes, "filtered-notifications" => Feature::FilteredNotifications, "get-pending" => Feature::GetPending, "instant-nodes" => Feature::InstantNodes, "item-ids" => Feature::ItemIDs, "last-published" => Feature::LastPublished, "leased-subscription" => Feature::LeasedSubscription, "manage-subscriptions" => Feature::ManageSubscriptions, "member-affiliation" => Feature::MemberAffiliation, "meta-data" => Feature::MetaData, "modify-affiliations" => Feature::ModifyAffiliations, "multi-collection" => Feature::MultiCollection, "multi-items" => Feature::MultiItems, "multi-subscribe" => Feature::MultiSubscribe, "outcast-affiliation" => Feature::OutcastAffiliation, "persistent-items" => Feature::PersistentItems, "presence-notifications" => Feature::PresenceNotifications, "presence-subscribe" => Feature::PresenceSubscribe, "publish" => Feature::Publish, "publish-options" => Feature::PublishOptions, "publish-only-affiliation" => Feature::PublishOnlyAffiliation, "publisher-affiliation" => Feature::PublisherAffiliation, "purge-nodes" => Feature::PurgeNodes, "retract-items" => Feature::RetractItems, "retrieve-affiliations" => Feature::RetrieveAffiliations, "retrieve-default" => Feature::RetrieveDefault, "retrieve-default-sub" => Feature::RetrieveDefaultSub, "retrieve-items" => Feature::RetrieveItems, "retrieve-subscriptions" => Feature::RetrieveSubscriptions, "subscribe" => Feature::Subscribe, "subscription-options" => Feature::SubscriptionOptions, "subscription-notifications" => Feature::SubscriptionNotifications, s => return Err(DeserializeError::FromStr(s.to_owned())), }) } }