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<Self> {
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<Self, Self::Err> {
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())),
})
}
}