use jid::JID; use stanza::xep_0030::{info, items}; pub use feature::Feature; pub use identity::Identity; #[derive(Debug, Clone)] pub struct Info { pub node: Option, pub features: Vec, pub identities: Vec, } impl From for Info { fn from(value: info::Query) -> Self { let features = value .features .into_iter() .map(|feature| feature.var) .collect(); let identities = value .identities .into_iter() .map(|identity| identity.into()) .collect(); Self { node: value.node, features, identities, } } } impl From for info::Query { fn from(value: Info) -> Self { let features = value .features .into_iter() .map(|feature| info::Feature { var: feature }) .collect(); let identities = value .identities .into_iter() .map(|identity| identity.into()) .collect(); Self { node: value.node, features, identities, extensions: Vec::new(), } } } #[derive(Debug, Clone)] pub struct Items { node: Option, items: Vec, } impl From for Items { fn from(value: items::Query) -> Self { let items = value.items.into_iter().map(|item| item.into()).collect(); Self { node: value.node, items, } } } impl From for items::Query { fn from(value: Items) -> Self { let items = value.items.into_iter().map(|item| item.into()).collect(); Self { node: value.node, items, } } } #[derive(Debug, Clone)] pub struct Item { jid: JID, name: Option, node: Option, } impl From for Item { fn from(value: items::Item) -> Self { Self { jid: value.jid, name: value.name, node: value.node, } } } impl From for items::Item { fn from(value: Item) -> Self { Self { jid: value.jid, name: value.name, node: value.node, } } } pub mod feature { use stanza::xep_0030::info; // https://xmpp.org/registrar/disco-features.html #[derive(Debug, Clone)] pub enum Feature { DNSSRV, FullUnicode, GC1, Activity, Address, Amp(Option), Bytestreams(Option), Caps(CapsVersion), ChatStates, Commands, Compress, Disco(Disco), FeatureNeg, Geoloc, HttpAuth, HttpBind, IBB, Mood, MUC(Option), Offline, PubSub(PubSub), RosterX, SIPub, SOAP(Option), WaitingList(Option), XHTMLIM, XDataLayout, XDataValidate, IPv6, Client, Component(Component), Iq(Iq), Server, X(X), MsgLog, MsgOffline, RosterDelimiter, SSLC2S, StringPrep, XMPPBind, XMPPE2E, XMPPSASL(Option), XMPPSession, XMPPStanzas, XMPPStreams, XMPPTLS(Option), RFC3264, Archive(Archive), Avatar(Avatar), Delay, Jingle(Jingle), Ping, Receipts, SSN, Time, XMLLang, VCardTemp, Styling(i8), SID(i8), Unknown(String), } impl From for Feature { fn from(value: info::Feature) -> Self { value.var.as_str().into() } } impl From for info::Feature { fn from(value: Feature) -> Self { Self { var: value.to_string(), } } } impl From<&str> for Feature { fn from(value: &str) -> Self { match value { "dnssrv" => Self::DNSSRV, "fullunicode" => Self::FullUnicode, "gc-1.0" => Self::GC1, "http://jabber.org/protocol/activity" => Self::Activity, "http://jabber.org/protocol/address" => Self::Address, "http://jabber.org/protocol/amp" => Self::Amp(None), "http://jabber.org/protocol/amp#errors" => Self::Amp(Some(Amp::Errors)), "http://jabber.org/protocol/amp?action=alert" => { Self::Amp(Some(Amp::Action(AmpAction::Alert))) } "http://jabber.org/protocol/amp?action=drop" => { Self::Amp(Some(Amp::Action(AmpAction::Drop))) } "http://jabber.org/protocol/amp?action=error" => { Self::Amp(Some(Amp::Action(AmpAction::Error))) } "http://jabber.org/protocol/amp?action=notify" => { Self::Amp(Some(Amp::Action(AmpAction::Notify))) } "http://jabber.org/protocol/amp?condition=deliver" => { Self::Amp(Some(Amp::Condition(AmpCondition::Deliver))) } "http://jabber.org/protocol/amp?condition=expire-at" => { Self::Amp(Some(Amp::Condition(AmpCondition::ExpireAt))) } "http://jabber.org/protocol/amp?condition=match-resource" => { Self::Amp(Some(Amp::Condition(AmpCondition::MatchResource))) } "http://jabber.org/protocol/bytestreams" => Self::Bytestreams(None), "http://jabber.org/protocol/bytestreams#udp" => { Self::Bytestreams(Some(Bytestreams::UDP)) } "http://jabber.org/protocol/caps" => Self::Caps(CapsVersion::One(None)), "http://jabber.org/protocol/caps#optimize" => { Self::Caps(CapsVersion::One(Some(CapsOne::Optimize))) } "http://jabber.org/protocol/chatstates" => Self::ChatStates, "http://jabber.org/protocol/commands" => Self::Commands, "http://jabber.org/protocol/compress" => Self::Compress, "http://jabber.org/protocol/disco#info" => Self::Disco(Disco::Info), "http://jabber.org/protocol/disco#items" => Self::Disco(Disco::Items), "http://jabber.org/protocol/feature-neg" => Self::FeatureNeg, "http://jabber.org/protocol/geoloc" => Self::Geoloc, "http://jabber.org/protocol/http-auth" => Self::HttpAuth, "http://jabber.org/protocol/httpbind" => Self::HttpBind, "http://jabber.org/protocol/ibb" => Self::IBB, "http://jabber.org/protocol/mood" => Self::Mood, "http://jabber.org/protocol/muc" => Self::MUC(None), "http://jabber.org/protocol/muc#admin" => Self::MUC(Some(MUC::Admin)), "http://jabber.org/protocol/muc#owner" => Self::MUC(Some(MUC::Owner)), "http://jabber.org/protocol/muc#register" => Self::MUC(Some(MUC::Register)), "http://jabber.org/protocol/muc#roomconfig" => Self::MUC(Some(MUC::RoomConfig)), "http://jabber.org/protocol/muc#roominfo" => Self::MUC(Some(MUC::RoomInfo)), "http://jabber.org/protocol/muc#user" => Self::MUC(Some(MUC::User)), "http://jabber.org/protocol/offline" => Self::Offline, "http://jabber.org/protocol/pubsub#access-authorize" => { Self::PubSub(PubSub::AccessAuthorize) } "http://jabber.org/protocol/pubsub#access-open" => Self::PubSub(PubSub::AccessOpen), "http://jabber.org/protocol/pubsub#access-presence" => { Self::PubSub(PubSub::AccessPresence) } "http://jabber.org/protocol/pubsub#access-roster" => { Self::PubSub(PubSub::AccessRoster) } "http://jabber.org/protocol/pubsub#access-whitelist" => { Self::PubSub(PubSub::AccessWhitelist) } "http://jabber.org/protocol/pubsub#auto-create" => Self::PubSub(PubSub::AutoCreate), "http://jabber.org/protocol/pubsub#auto-subscribe" => { Self::PubSub(PubSub::AutoSubscribe) } "http://jabber.org/protocol/pubsub#collections" => { Self::PubSub(PubSub::Collections) } "http://jabber.org/protocol/pubsub#config-node" => Self::PubSub(PubSub::ConfigNode), "http://jabber.org/protocol/pubsub#create-and-configure" => { Self::PubSub(PubSub::CreateAndConfigure) } "http://jabber.org/protocol/pubsub#create-nodes" => { Self::PubSub(PubSub::CreateNodes) } "http://jabber.org/protocol/pubsub#delete-any" => Self::PubSub(PubSub::DeleteAny), "http://jabber.org/protocol/pubsub#delete-nodes" => { Self::PubSub(PubSub::DeleteNodes) } "http://jabber.org/protocol/pubsub#filtered-notifications" => { Self::PubSub(PubSub::FilteredNotifications) } "http://jabber.org/protocol/pubsub#get-pending" => Self::PubSub(PubSub::GetPending), "http://jabber.org/protocol/pubsub#instant-nodes" => { Self::PubSub(PubSub::InstantNodes) } "http://jabber.org/protocol/pubsub#item-ids" => Self::PubSub(PubSub::ItemIDs), "http://jabber.org/protocol/pubsub#last-published" => { Self::PubSub(PubSub::LastPublished) } "http://jabber.org/protocol/pubsub#leased-subscription" => { Self::PubSub(PubSub::LeasedSubscription) } "http://jabber.org/protocol/pubsub#manage-subscription" => { Self::PubSub(PubSub::ManageSubscription) } "http://jabber.org/protocol/pubsub#member-affiliation" => { Self::PubSub(PubSub::MemberAffiliation) } "http://jabber.org/protocol/pubsub#meta-data" => Self::PubSub(PubSub::MetaData), "http://jabber.org/protocol/pubsub#modify-affiliations" => { Self::PubSub(PubSub::ModifyAffiliations) } "http://jabber.org/protocol/pubsub#multi-collection" => { Self::PubSub(PubSub::MultiCollection) } "http://jabber.org/protocol/pubsub#multi-subscribe" => { Self::PubSub(PubSub::MultiSubscribe) } "http://jabber.org/protocol/pubsub#outcast-affiliation" => { Self::PubSub(PubSub::OutcastAffiliation) } "http://jabber.org/protocol/pubsub#persistent-items" => { Self::PubSub(PubSub::PersistentItems) } "http://jabber.org/protocol/pubsub#presence-notifications" => { Self::PubSub(PubSub::PresenceNotifications) } "http://jabber.org/protocol/pubsub#presence-subscribe" => { Self::PubSub(PubSub::PresenceSubscribe) } "http://jabber.org/protocol/pubsub#publish" => Self::PubSub(PubSub::Publish), "http://jabber.org/protocol/pubsub#publish-options" => { Self::PubSub(PubSub::PublishOptions) } "http://jabber.org/protocol/pubsub#publisher-affiliation" => { Self::PubSub(PubSub::PublisherAffiliation) } "http://jabber.org/protocol/pubsub#purge-nodes" => Self::PubSub(PubSub::PurgeNodes), "http://jabber.org/protocol/pubsub#retract-items" => { Self::PubSub(PubSub::RetractItems) } "http://jabber.org/protocol/pubsub#retrieve-affiliations" => { Self::PubSub(PubSub::RetrieveAffiliations) } "http://jabber.org/protocol/pubsub#retrieve-default" => { Self::PubSub(PubSub::RetrieveDefault) } "http://jabber.org/protocol/pubsub#retrieve-items" => { Self::PubSub(PubSub::RetrieveItems) } "http://jabber.org/protocol/pubsub#retrieve-subscriptions" => { Self::PubSub(PubSub::RetrieveSubscriptions) } "http://jabber.org/protocol/pubsub#subscribe" => Self::PubSub(PubSub::Subscribe), "http://jabber.org/protocol/pubsub#subscription-options" => { Self::PubSub(PubSub::SubscriptionOptions) } "http://jabber.org/protocol/pubsub#subscription-notifications" => { Self::PubSub(PubSub::SubscriptionNotifications) } "http://jabber.org/protocol/rosterx" => Self::RosterX, "http://jabber.org/protocol/sipub" => Self::SIPub, "http://jabber.org/protocol/soap" => Self::SOAP(None), "http://jabber.org/protocol/soap#fault" => Self::SOAP(Some(SOAP::Fault)), "http://jabber.org/protocol/waitinglist" => Self::WaitingList(None), "http://jabber.org/protocol/waitinglist/schemes/mailto" => { Self::WaitingList(Some(WaitingList::Schemes(WaitingListSchemes::Mailto))) } "http://jabber.org/protocol/waitinglist/schemes/tel" => { Self::WaitingList(Some(WaitingList::Schemes(WaitingListSchemes::Tel))) } "http://jabber.org/protocol/xhtml-im" => Self::XHTMLIM, "http://jabber.org/protocol/xdata-layout" => Self::XDataLayout, "http://jabber.org/protocol/xdata-validate" => Self::XDataValidate, "ipv6" => Self::IPv6, "jabber:client" => Self::Client, "jabber:component:accept" => Self::Component(Component::Accept), "jabber:component:connect" => Self::Component(Component::Connect), "jabber:iq:auth" => Self::Iq(Iq::Auth), "jabber:iq:gateway" => Self::Iq(Iq::Gateway), "jabber:iq:last" => Self::Iq(Iq::Last), "jabber:iq:oob" => Self::Iq(Iq::OOB), "jabber:iq:privacy" => Self::Iq(Iq::Privacy), "jabber:iq:private" => Self::Iq(Iq::Private), "jabber:iq:register" => Self::Iq(Iq::Register), "jabber:iq:roster" => Self::Iq(Iq::Roster), "jabber:iq:rpc" => Self::Iq(Iq::RPC), "jabber:iq:search" => Self::Iq(Iq::Search), "jabber:iq:version" => Self::Iq(Iq::Version), "jabber:server" => Self::Server, "jabber:x:data" => Self::X(X::Data), "jabber:x:encrypted" => Self::X(X::Encrypted), "jabber:x:oob" => Self::X(X::OOB), "jabber:x:signed" => Self::X(X::Signed), "msglog" => Self::MsgLog, "msgoffline" => Self::MsgOffline, "muc_hidden" => Self::MUC(Some(MUC::Hidden)), "muc_membersonly" => Self::MUC(Some(MUC::MembersOnly)), "muc_moderated" => Self::MUC(Some(MUC::Moderated)), "muc_nonanonymous" => Self::MUC(Some(MUC::NonAnonymous)), "muc_open" => Self::MUC(Some(MUC::Open)), "muc_passwordprotected" => Self::MUC(Some(MUC::PasswordProtected)), "muc_persistent" => Self::MUC(Some(MUC::Persistent)), "muc_public" => Self::MUC(Some(MUC::Public)), "muc_rooms" => Self::MUC(Some(MUC::Rooms)), "muc_semianonymous" => Self::MUC(Some(MUC::SemiAnonymous)), "muc_temporary" => Self::MUC(Some(MUC::Temporary)), "muc_unmoderated" => Self::MUC(Some(MUC::Unmoderated)), "muc_unsecured" => Self::MUC(Some(MUC::Unsecured)), "roster:delimiter" => Self::RosterDelimiter, "sslc2s" => Self::SSLC2S, "stringprep" => Self::StringPrep, "urn:ietf:params:xml:ns:xmpp-bind" => Self::XMPPBind, "urn:ietf:params:xml:ns:xmpp-e2e" => Self::XMPPE2E, "urn:ietf:params:xml:ns:xmpp-sasl" => Self::XMPPSASL(None), "urn:ietf:params:xml:ns:xmpp-sasl#c2s" => Self::XMPPSASL(Some(XMPPSASL::C2S)), "urn:ietf:params:xml:ns:xmpp-sasl#s2s" => Self::XMPPSASL(Some(XMPPSASL::S2S)), "urn:ietf:params:xml:ns:xmpp-session" => Self::XMPPSession, "urn:ietf:params:xml:ns:xmpp-stanzas" => Self::XMPPStanzas, "urn:ietf:params:xml:ns:xmpp-streams" => Self::XMPPStreams, "urn:ietf:params:xml:ns:xmpp-tls" => Self::XMPPTLS(None), "urn:ietf:params:xml:ns:xmpp-tls#c2s" => Self::XMPPTLS(Some(XMPPTLS::C2S)), "urn:ietf:params:xml:ns:xmpp-tls#s2s" => Self::XMPPTLS(Some(XMPPTLS::S2S)), "urn:ietf:rfc:3264" => Self::RFC3264, "urn:xmpp:archive:auto" => Self::Archive(Archive::Auto), "urn:xmpp:archive:manage" => Self::Archive(Archive::Manage), "urn:xmpp:archive:manual" => Self::Archive(Archive::Manual), "urn:xmpp:archive:pref" => Self::Archive(Archive::Pref), "urn:xmpp:avatar:data" => Self::Avatar(Avatar::Data), "urn:xmpp:avatar:metadata" => Self::Avatar(Avatar::Metadata), "urn:xmpp:delay" => Self::Delay, "urn:xmpp:jingle:apps:rtp:audio" => { Self::Jingle(Jingle::Apps(JingleApps::RTP(JingleAppsRTP::Audio))) } "urn:xmpp:jingle:apps:rtp:video" => { Self::Jingle(Jingle::Apps(JingleApps::RTP(JingleAppsRTP::Video))) } "urn:xmpp:ping" => Self::Ping, "urn:xmpp:receipts" => Self::Receipts, "urn:xmpp:ssn" => Self::SSN, "urn:xmpp:time" => Self::Time, "xmllang" => Self::XMLLang, "vcard-temp" => Self::VCardTemp, "urn:xmpp:styling:0" => Self::Styling(0), "urn:xmpp:sid:0" => Self::SID(0), "urn:xmpp:caps" => Self::Caps(CapsVersion::Two(None)), "urn:xmpp:caps:optimize" => Self::Caps(CapsVersion::Two(Some(CapsTwo::Optimize))), s => Self::Unknown(s.to_owned()), } } } impl ToString for Feature { fn to_string(&self) -> String { match self { Feature::DNSSRV => "dnssrv".to_owned(), Feature::FullUnicode => "fullunicode".to_owned(), Feature::GC1 => "gc-1.0".to_owned(), Feature::Activity => "http://jabber.org/protocol/activity".to_owned(), Feature::Address => "http://jabber.org/protocol/address".to_owned(), Feature::Amp(amp) => match amp { Some(a) => a.to_string(), None => "http://jabber.org/protocol/amp".to_owned(), }, Feature::Bytestreams(byte_streams) => match byte_streams { Some(b) => b.to_string(), None => "http://jabber.org/protocol/bytestreams".to_owned(), }, Feature::Caps(caps_version) => caps_version.to_string(), Feature::ChatStates => "http://jabber.org/protocol/chatstates".to_owned(), Feature::Commands => "http://jabber.org/protocol/commands".to_owned(), Feature::Compress => "http://jabber.org/protocol/compress".to_owned(), Feature::Disco(disco) => disco.to_string(), Feature::FeatureNeg => "http://jabber.org/protocol/feature-neg".to_owned(), Feature::Geoloc => "http://jabber.org/protocol/geoloc".to_owned(), Feature::HttpAuth => "http://jabber.org/protocol/http-auth".to_owned(), Feature::HttpBind => "http://jabber.org/protocol/httpbind".to_owned(), Feature::IBB => "http://jabber.org/protocol/ibb".to_owned(), Feature::Mood => "http://jabber.org/protocol/mood".to_owned(), Feature::MUC(muc) => match muc { Some(m) => m.to_string(), None => "http://jabber.org/protocol/muc".to_owned(), }, Feature::Offline => "http://jabber.org/protocol/offline".to_owned(), Feature::PubSub(pub_sub) => pub_sub.to_string(), Feature::RosterX => "http://jabber.org/protocol/rosterx".to_owned(), Feature::SIPub => "http://jabber.org/protocol/sipub".to_owned(), Feature::SOAP(soap) => match soap { Some(s) => s.to_string(), None => "http://jabber.org/protocol/soap".to_owned(), }, Feature::WaitingList(waiting_list) => match waiting_list { Some(w) => w.to_string(), None => "http://jabber.org/protocol/waitinglist".to_owned(), }, Feature::XHTMLIM => "http://jabber.org/protocol/xhtml-im".to_owned(), Feature::XDataLayout => "http://jabber.org/protocol/xdata-layout".to_owned(), Feature::XDataValidate => "http://jabber.org/protocol/xdata-validate".to_owned(), Feature::IPv6 => "ipv6".to_owned(), Feature::Client => "jabber:client".to_owned(), Feature::Component(component) => component.to_string(), Feature::Iq(iq) => iq.to_string(), Feature::Server => "jabber:server".to_owned(), Feature::X(x) => x.to_string(), Feature::MsgLog => "msglog".to_owned(), Feature::MsgOffline => "msgoffline".to_owned(), Feature::RosterDelimiter => "roster:delimiter".to_owned(), Feature::SSLC2S => "sslc2s".to_owned(), Feature::StringPrep => "stringprep".to_owned(), Feature::XMPPBind => "urn:ietf:params:xml:ns:xmpp-bind".to_owned(), Feature::XMPPE2E => "urn:ietf:params:xml:ns:xmpp-e2e".to_owned(), Feature::XMPPSASL(xmppsasl) => match xmppsasl { Some(x) => x.to_string(), None => "urn:ietf:params:xml:ns:xmpp-sasl".to_owned(), }, Feature::XMPPSession => "urn:ietf:params:xml:ns:xmpp-session".to_owned(), Feature::XMPPStanzas => "urn:ietf:params:xml:ns:xmpp-stanzas".to_owned(), Feature::XMPPStreams => "urn:ietf:params:xml:ns:xmpp-streams".to_owned(), Feature::XMPPTLS(xmpptls) => match xmpptls { Some(x) => x.to_string(), None => "urn:ietf:params:xml:ns:xmpp-tls".to_owned(), }, Feature::RFC3264 => "urn:ietf:rfc:3264".to_owned(), Feature::Archive(archive) => archive.to_string(), Feature::Avatar(avatar) => avatar.to_string(), Feature::Delay => "urn:xmpp:delay".to_owned(), Feature::Jingle(jingle) => jingle.to_string(), Feature::Ping => "urn:xmpp:ping".to_owned(), Feature::Receipts => "urn:xmpp:receipts".to_owned(), Feature::SSN => "urn:xmpp:ssn".to_owned(), Feature::Time => "urn:xmpp:time".to_owned(), Feature::XMLLang => "xmllang".to_owned(), Feature::VCardTemp => "vcard-temp".to_owned(), Feature::Styling(_) => "urn:xmpp:styling:0".to_owned(), Feature::SID(_) => "urn:xmpp:sid:0".to_owned(), Feature::Unknown(s) => s.to_owned(), } } } #[derive(Debug, Clone)] pub enum Amp { Errors, Action(AmpAction), Condition(AmpCondition), } impl ToString for Amp { fn to_string(&self) -> String { match self { Amp::Errors => "http://jabber.org/protocol/amp#errors".to_owned(), Amp::Action(amp_action) => amp_action.to_string(), Amp::Condition(amp_condition) => amp_condition.to_string(), } } } #[derive(Debug, Clone)] pub enum AmpAction { Alert, Drop, Error, Notify, } impl ToString for AmpAction { fn to_string(&self) -> String { match self { AmpAction::Alert => "http://jabber.org/protocol/amp?action=alert", AmpAction::Drop => "http://jabber.org/protocol/amp?action=drop", AmpAction::Error => "http://jabber.org/protocol/amp?action=error", AmpAction::Notify => "http://jabber.org/protocol/amp?action=notify", } .to_owned() } } #[derive(Debug, Clone)] pub enum AmpCondition { Deliver, ExpireAt, MatchResource, } impl ToString for AmpCondition { fn to_string(&self) -> String { match self { AmpCondition::Deliver => "http://jabber.org/protocol/amp?condition=deliver", AmpCondition::ExpireAt => "http://jabber.org/protocol/amp?condition=expire-at", AmpCondition::MatchResource => { "http://jabber.org/protocol/amp?condition=match-resource" } } .to_owned() } } #[derive(Debug, Clone)] pub enum Bytestreams { UDP, } impl ToString for Bytestreams { fn to_string(&self) -> String { match self { Bytestreams::UDP => "http://jabber.org/protocol/bytestreams#udp", } .to_owned() } } #[derive(Debug, Clone)] pub enum CapsVersion { One(Option), Two(Option), } impl ToString for CapsVersion { fn to_string(&self) -> String { match self { CapsVersion::One(caps_one) => match caps_one { Some(c) => c.to_string(), None => "http://jabber.org/protocol/caps".to_owned(), }, CapsVersion::Two(caps_two) => match caps_two { Some(c) => c.to_string(), None => "urn:xmpp:caps".to_owned(), }, } } } #[derive(Debug, Clone)] pub enum CapsOne { Optimize, } impl ToString for CapsOne { fn to_string(&self) -> String { match self { CapsOne::Optimize => "http://jabber.org/protocol/caps#optimize", } .to_string() } } #[derive(Debug, Clone)] pub enum CapsTwo { Optimize, } impl ToString for CapsTwo { fn to_string(&self) -> String { match self { CapsTwo::Optimize => "urn:xmpp:caps:optimize", } .to_owned() } } #[derive(Debug, Clone)] pub enum Disco { Info, Items, } impl ToString for Disco { fn to_string(&self) -> String { match self { Disco::Info => "http://jabber.org/protocol/disco#info", Disco::Items => "http://jabber.org/protocol/disco#items", } .to_owned() } } #[derive(Debug, Clone)] pub enum MUC { Admin, Owner, Register, RoomConfig, RoomInfo, User, Hidden, MembersOnly, Moderated, NonAnonymous, Open, PasswordProtected, Persistent, Public, Rooms, SemiAnonymous, Temporary, Unmoderated, Unsecured, } impl ToString for MUC { fn to_string(&self) -> String { match self { MUC::Admin => "http://jabber.org/protocol/muc#admin", MUC::Owner => "http://jabber.org/protocol/muc#owner", MUC::Register => "http://jabber.org/protocol/muc#register", MUC::RoomConfig => "http://jabber.org/protocol/muc#roomconfig", MUC::RoomInfo => "http://jabber.org/protocol/muc#roominfo", MUC::User => "http://jabber.org/protocol/muc#user", MUC::Hidden => "muc_hidden", MUC::MembersOnly => "muc_membersonly", MUC::Moderated => "muc_moderated", MUC::NonAnonymous => "muc_nonanonymous", MUC::Open => "muc_open", MUC::PasswordProtected => "muc_passwordprotected", MUC::Persistent => "muc_persistent", MUC::Public => "muc_public", MUC::Rooms => "muc_rooms", MUC::SemiAnonymous => "muc_semianonymous", MUC::Temporary => "muc_temporary", MUC::Unmoderated => "muc_unmoderated", MUC::Unsecured => "muc_unsecured", } .to_owned() } } #[derive(Debug, Clone)] pub enum PubSub { AccessAuthorize, AccessOpen, AccessPresence, AccessRoster, AccessWhitelist, AutoCreate, AutoSubscribe, Collections, ConfigNode, CreateAndConfigure, CreateNodes, DeleteAny, DeleteNodes, FilteredNotifications, GetPending, InstantNodes, ItemIDs, LastPublished, LeasedSubscription, ManageSubscription, MemberAffiliation, MetaData, ModifyAffiliations, MultiCollection, MultiSubscribe, OutcastAffiliation, PersistentItems, PresenceNotifications, PresenceSubscribe, Publish, PublishOptions, PublisherAffiliation, PurgeNodes, RetractItems, RetrieveAffiliations, RetrieveDefault, RetrieveItems, RetrieveSubscriptions, Subscribe, SubscriptionOptions, SubscriptionNotifications, } impl ToString for PubSub { fn to_string(&self) -> String { match self { PubSub::AccessAuthorize => "http://jabber.org/protocol/pubsub#access-authorize", PubSub::AccessOpen => "http://jabber.org/protocol/pubsub#access-open", PubSub::AccessPresence => "http://jabber.org/protocol/pubsub#access-presence", PubSub::AccessRoster => "http://jabber.org/protocol/pubsub#access-roster", PubSub::AccessWhitelist => "http://jabber.org/protocol/pubsub#access-whitelist", PubSub::AutoCreate => "http://jabber.org/protocol/pubsub#auto-create", PubSub::AutoSubscribe => "http://jabber.org/protocol/pubsub#auto-subscribe", PubSub::Collections => "http://jabber.org/protocol/pubsub#collections", PubSub::ConfigNode => "http://jabber.org/protocol/pubsub#config-node", PubSub::CreateAndConfigure => { "http://jabber.org/protocol/pubsub#create-and-configure" } PubSub::CreateNodes => "http://jabber.org/protocol/pubsub#create-nodes", PubSub::DeleteAny => "http://jabber.org/protocol/pubsub#delete-any", PubSub::DeleteNodes => "http://jabber.org/protocol/pubsub#delete-nodes", PubSub::FilteredNotifications => { "http://jabber.org/protocol/pubsub#filtered-notifications" } PubSub::GetPending => "http://jabber.org/protocol/pubsub#get-pending", PubSub::InstantNodes => "http://jabber.org/protocol/pubsub#instant-nodes", PubSub::ItemIDs => "http://jabber.org/protocol/pubsub#item-ids", PubSub::LastPublished => "http://jabber.org/protocol/pubsub#last-published", PubSub::LeasedSubscription => { "http://jabber.org/protocol/pubsub#leased-subscription" } PubSub::ManageSubscription => { "http://jabber.org/protocol/pubsub#manage-subscription" } PubSub::MemberAffiliation => "http://jabber.org/protocol/pubsub#member-affiliation", PubSub::MetaData => "http://jabber.org/protocol/pubsub#meta-data", PubSub::ModifyAffiliations => { "http://jabber.org/protocol/pubsub#modify-affiliations" } PubSub::MultiCollection => "http://jabber.org/protocol/pubsub#multi-collection", PubSub::MultiSubscribe => "http://jabber.org/protocol/pubsub#multi-subscribe", PubSub::OutcastAffiliation => { "http://jabber.org/protocol/pubsub#outcast-affiliation" } PubSub::PersistentItems => "http://jabber.org/protocol/pubsub#persistent-items", PubSub::PresenceNotifications => { "http://jabber.org/protocol/pubsub#presence-notifications" } PubSub::PresenceSubscribe => "http://jabber.org/protocol/pubsub#presence-subscribe", PubSub::Publish => "http://jabber.org/protocol/pubsub#publish", PubSub::PublishOptions => "http://jabber.org/protocol/pubsub#publish-options", PubSub::PublisherAffiliation => { "http://jabber.org/protocol/pubsub#publisher-affiliation" } PubSub::PurgeNodes => "http://jabber.org/protocol/pubsub#purge-nodes", PubSub::RetractItems => "http://jabber.org/protocol/pubsub#retract-items", PubSub::RetrieveAffiliations => { "http://jabber.org/protocol/pubsub#retrieve-affiliations" } PubSub::RetrieveDefault => "http://jabber.org/protocol/pubsub#retrieve-default", PubSub::RetrieveItems => "http://jabber.org/protocol/pubsub#retrieve-items", PubSub::RetrieveSubscriptions => { "http://jabber.org/protocol/pubsub#retrieve-subscriptions" } PubSub::Subscribe => "http://jabber.org/protocol/pubsub#subscribe", PubSub::SubscriptionOptions => { "http://jabber.org/protocol/pubsub#subscription-options" } PubSub::SubscriptionNotifications => { "http://jabber.org/protocol/pubsub#subscription-notifications" } } .to_owned() } } #[derive(Debug, Clone)] pub enum SOAP { Fault, } impl ToString for SOAP { fn to_string(&self) -> String { match self { SOAP::Fault => "http://jabber.org/protocol/soap#fault", } .to_owned() } } #[derive(Debug, Clone)] pub enum WaitingList { Schemes(WaitingListSchemes), } impl ToString for WaitingList { fn to_string(&self) -> String { match self { WaitingList::Schemes(waiting_list_schemes) => waiting_list_schemes.to_string(), } } } #[derive(Debug, Clone)] pub enum WaitingListSchemes { Mailto, Tel, } impl ToString for WaitingListSchemes { fn to_string(&self) -> String { match self { WaitingListSchemes::Mailto => { "http://jabber.org/protocol/waitinglist/schemes/mailto" } WaitingListSchemes::Tel => "http://jabber.org/protocol/waitinglist/schemes/tel", } .to_owned() } } #[derive(Debug, Clone)] pub enum Component { Accept, Connect, } impl ToString for Component { fn to_string(&self) -> String { match self { Component::Accept => "jabber:component:accept", Component::Connect => "jabber:component:connect", } .to_owned() } } #[derive(Debug, Clone)] pub enum Iq { Auth, Gateway, Last, OOB, Privacy, Private, Register, Roster, RPC, Search, Version, } impl ToString for Iq { fn to_string(&self) -> String { match self { Iq::Auth => "jabber:iq:auth", Iq::Gateway => "jabber:iq:gateway", Iq::Last => "jabber:iq:last", Iq::OOB => "jabber:iq:oob", Iq::Privacy => "jabber:iq:privacy", Iq::Private => "jabber:iq:private", Iq::Register => "jabber:iq:register", Iq::Roster => "jabber:iq:roster", Iq::RPC => "jabber:iq:rpc", Iq::Search => "jabber:iq:search", Iq::Version => "jabber:iq:version", } .to_owned() } } #[derive(Debug, Clone)] pub enum X { Data, Encrypted, OOB, Signed, } impl ToString for X { fn to_string(&self) -> String { match self { X::Data => "jabber:x:data", X::Encrypted => "jabber:x:encrypted", X::OOB => "jabber:x:oob", X::Signed => "jabber:x:signed", } .to_owned() } } #[derive(Debug, Clone)] pub enum XMPPSASL { C2S, S2S, } impl ToString for XMPPSASL { fn to_string(&self) -> String { match self { XMPPSASL::C2S => "urn:ietf:params:xml:ns:xmpp-sasl#c2s", XMPPSASL::S2S => "urn:ietf:params:xml:ns:xmpp-sasl#s2s", } .to_owned() } } #[derive(Debug, Clone)] pub enum XMPPTLS { C2S, S2S, } impl ToString for XMPPTLS { fn to_string(&self) -> String { match self { XMPPTLS::C2S => "urn:ietf:params:xml:ns:xmpp-tls#c2s", XMPPTLS::S2S => "urn:ietf:params:xml:ns:xmpp-tls#s2s", } .to_owned() } } #[derive(Debug, Clone)] pub enum Archive { Auto, Manage, Manual, Pref, } impl ToString for Archive { fn to_string(&self) -> String { match self { Archive::Auto => "urn:xmpp:archive:auto", Archive::Manage => "urn:xmpp:archive:manage", Archive::Manual => "urn:xmpp:archive:manual", Archive::Pref => "urn:xmpp:archive:pref", } .to_owned() } } #[derive(Debug, Clone)] pub enum Avatar { Data, Metadata, } impl ToString for Avatar { fn to_string(&self) -> String { match self { Avatar::Data => "urn:xmpp:avatar:data", Avatar::Metadata => "urn:xmpp:avatar:metadata", } .to_owned() } } #[derive(Debug, Clone)] pub enum Jingle { Apps(JingleApps), } impl ToString for Jingle { fn to_string(&self) -> String { match self { Jingle::Apps(jingle_apps) => jingle_apps.to_string(), } } } #[derive(Debug, Clone)] pub enum JingleApps { RTP(JingleAppsRTP), } impl ToString for JingleApps { fn to_string(&self) -> String { match self { JingleApps::RTP(jingle_apps_rtp) => jingle_apps_rtp.to_string(), } } } #[derive(Debug, Clone)] pub enum JingleAppsRTP { Audio, Video, } impl ToString for JingleAppsRTP { fn to_string(&self) -> String { match self { JingleAppsRTP::Audio => "urn:xmpp:jingle:apps:rtp:audio", JingleAppsRTP::Video => "urn:xmpp:jingle:apps:rtp:video", } .to_owned() } } } pub mod identity { use stanza::xep_0030::info; #[derive(Debug, Clone)] pub struct Identity { pub name: Option, pub category: Category, } impl From for Identity { fn from(value: info::Identity) -> Self { let category = Category::from_category_and_type(&value.category, &value.r#type); Self { name: value.name, category, } } } impl From for info::Identity { fn from(value: Identity) -> Self { Self { category: value.category.to_string(), name: value.name, r#type: value.category.r#type(), lang: None, } } } // TODO: separate crate for disco registry /// categories taken from [XMPP Disco Categories](https://xmpp.org/registrar/disco-categories.html) #[derive(Debug, Clone)] pub enum Category { Account(Account), Auth(Auth), Authz(Authz), Automation(Automation), Client(Client), Collaboration(Collaboration), Component(Component), Conference(Conference), Directory(Directory), Gateway(Gateway), Headline(Headline), Hierarchy(Hierarchy), Proxy(Proxy), PubSub(PubSub), Server(Server), Store(Store), Other { category: String, r#type: String }, } impl ToString for Category { fn to_string(&self) -> String { match self { Category::Account(_account) => "account", Category::Auth(_auth) => "auth", Category::Authz(_authz) => "authz", Category::Automation(_automation) => "automation", Category::Client(_client) => "client", Category::Collaboration(_collaboration) => "collaboration", Category::Component(_component) => "component", Category::Conference(_conference) => "conference", Category::Directory(_directory) => "directory", Category::Gateway(_gateway) => "gateway", Category::Headline(_headline) => "headline", Category::Hierarchy(_hierarchy) => "hierarchy", Category::Proxy(_proxy) => "proxy", Category::PubSub(_pub_sub) => "pubsub", Category::Server(_server) => "server", Category::Store(_store) => "store", Category::Other { category, r#type: _, } => category, } .to_owned() } } impl Category { pub fn from_category_and_type(category: &str, r#type: &str) -> Self { match category { "account" => Self::Account(r#type.into()), "auth" => Self::Auth(r#type.into()), "authz" => Self::Authz(r#type.into()), "automation" => Self::Automation(r#type.into()), "client" => Self::Client(r#type.into()), "collaboration" => Self::Collaboration(r#type.into()), "component" => Self::Component(r#type.into()), "conference" => Self::Conference(r#type.into()), "directory" => Self::Directory(r#type.into()), "gateway" => Self::Gateway(r#type.into()), "headline" => Self::Headline(r#type.into()), "hierarchy" => Self::Hierarchy(r#type.into()), "proxy" => Self::Proxy(r#type.into()), "pubsub" => Self::PubSub(r#type.into()), "server" => Self::Server(r#type.into()), "store" => Self::Store(r#type.into()), s => Self::Other { category: s.to_owned(), r#type: r#type.to_owned(), }, } } pub fn r#type(&self) -> String { match self { Category::Account(account) => account.to_string(), Category::Auth(auth) => auth.to_string(), Category::Authz(authz) => authz.to_string(), Category::Automation(automation) => automation.to_string(), Category::Client(client) => client.to_string(), Category::Collaboration(collaboration) => collaboration.to_string(), Category::Component(component) => component.to_string(), Category::Conference(conference) => conference.to_string(), Category::Directory(directory) => directory.to_string(), Category::Gateway(gateway) => gateway.to_string(), Category::Headline(headline) => headline.to_string(), Category::Hierarchy(hierarchy) => hierarchy.to_string(), Category::Proxy(proxy) => proxy.to_string(), Category::PubSub(pub_sub) => pub_sub.to_string(), Category::Server(server) => server.to_string(), Category::Store(store) => store.to_string(), Category::Other { category: _, r#type, } => r#type.to_owned(), } } } #[derive(Debug, Clone)] pub enum Account { Admin, Anonymous, Registered, Other(String), } impl ToString for Account { fn to_string(&self) -> String { match self { Account::Admin => "admin", Account::Anonymous => "anonymous", Account::Registered => "registered", Account::Other(s) => s, } .to_owned() } } impl From<&str> for Account { fn from(value: &str) -> Self { match value { "admin" => Self::Admin, "anonymous" => Self::Anonymous, "registered" => Self::Registered, s => Self::Other(s.to_string()), } } } #[derive(Debug, Clone)] pub enum Auth { Cert, Generic, LDAP, NTLM, PAM, Radius, Other(String), } impl ToString for Auth { fn to_string(&self) -> String { match self { Auth::Cert => "cert", Auth::Generic => "generic", Auth::LDAP => "ldap", Auth::NTLM => "ntlm", Auth::PAM => "pam", Auth::Radius => "radius", Auth::Other(s) => s, } .to_owned() } } impl From<&str> for Auth { fn from(value: &str) -> Self { match value { "cert" => Self::Cert, "generic" => Self::Generic, "ldap" => Self::LDAP, "ntlm" => Self::NTLM, "pam" => Self::PAM, "radius" => Self::Radius, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Authz { Ephemeral, Other(String), } impl ToString for Authz { fn to_string(&self) -> String { match self { Authz::Ephemeral => "ephemeral", Authz::Other(s) => s, } .to_owned() } } impl From<&str> for Authz { fn from(value: &str) -> Self { match value { "ephemeral" => Self::Ephemeral, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Automation { CommandList, CommandNode, RPC, SOAP, Translation, Other(String), } impl ToString for Automation { fn to_string(&self) -> String { match self { Automation::CommandList => "command-list", Automation::CommandNode => "command-node", Automation::RPC => "rpc", Automation::SOAP => "soap", Automation::Translation => "translation", Automation::Other(s) => s, } .to_owned() } } impl From<&str> for Automation { fn from(value: &str) -> Self { match value { "command-list" => Self::CommandList, "command-node" => Self::CommandNode, "rpc" => Self::RPC, "soap" => Self::SOAP, "translation" => Self::Translation, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Client { Bot, Console, Game, Handheld, PC, Phone, SMS, Tablet, Web, Other(String), } impl ToString for Client { fn to_string(&self) -> String { match self { Client::Bot => "bot", Client::Console => "console", Client::Game => "game", Client::Handheld => "handheld", Client::PC => "pc", Client::Phone => "phone", Client::SMS => "sms", Client::Tablet => "tablet", Client::Web => "web", Client::Other(s) => s, } .to_owned() } } impl From<&str> for Client { fn from(value: &str) -> Self { match value { "bot" => Self::Bot, "console" => Self::Console, "game" => Self::Game, "handheld" => Self::Handheld, "pc" => Self::PC, "phone" => Self::Phone, "sms" => Self::SMS, "tablet" => Self::Tablet, "web" => Self::Web, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Collaboration { Whiteboard, Other(String), } impl ToString for Collaboration { fn to_string(&self) -> String { match self { Collaboration::Whiteboard => "whiteboard", Collaboration::Other(s) => s, } .to_owned() } } impl From<&str> for Collaboration { fn from(value: &str) -> Self { match value { "whiteboard" => Self::Whiteboard, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Component { Archive, C2S, Generic, Load, Log, Presence, Router, S2S, SM, Stats, Other(String), } impl ToString for Component { fn to_string(&self) -> String { match self { Component::Archive => "archive", Component::C2S => "c2s", Component::Generic => "generic", Component::Load => "load", Component::Log => "log", Component::Presence => "presence", Component::Router => "router", Component::S2S => "s2s", Component::SM => "sm", Component::Stats => "stats", Component::Other(s) => s, } .to_owned() } } impl From<&str> for Component { fn from(value: &str) -> Self { match value { "archive" => Self::Archive, "c2s" => Self::C2S, "generic" => Self::Generic, "load" => Self::Load, "log" => Self::Log, "presence" => Self::Presence, "router" => Self::Router, "s2s" => Self::S2S, "sm" => Self::SM, "stats" => Self::Stats, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Conference { IRC, Text, Other(String), } impl ToString for Conference { fn to_string(&self) -> String { match self { Conference::IRC => "irc", Conference::Text => "text", Conference::Other(s) => s, } .to_owned() } } impl From<&str> for Conference { fn from(value: &str) -> Self { match value { "irc" => Self::IRC, "text" => Self::Text, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Directory { Chatroom, Group, User, WaitingList, Other(String), } impl ToString for Directory { fn to_string(&self) -> String { match self { Directory::Chatroom => "chatroom", Directory::Group => "group", Directory::User => "user", Directory::WaitingList => "waiting-list", Directory::Other(s) => s, } .to_string() } } impl From<&str> for Directory { fn from(value: &str) -> Self { match value { "chatroom" => Self::Chatroom, "group" => Self::Group, "user" => Self::User, "waiting-list" => Self::WaitingList, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Gateway { Aim, Discord, Facebook, GaduGadu, HttpWS, ICQ, IRC, LCS, Mattermost, MRIM, MSN, MySpaceIM, OCS, PSTN, QQ, Sametime, Signal, SIMPLE, Skype, SMS, SMTP, Steam, Telegram, Tlen, Xfire, XMPP, Yahoo, Other(String), } impl ToString for Gateway { fn to_string(&self) -> String { match self { Gateway::Aim => "aim", Gateway::Discord => "discord", Gateway::Facebook => "facebook", Gateway::GaduGadu => "gadu-gadu", Gateway::HttpWS => "http-ws", Gateway::ICQ => "icq", Gateway::IRC => "irc", Gateway::LCS => "lcs", Gateway::Mattermost => "mattermost", Gateway::MRIM => "mrim", Gateway::MSN => "msn", Gateway::MySpaceIM => "myspaceim", Gateway::OCS => "ocs", Gateway::PSTN => "pstn", Gateway::QQ => "qq", Gateway::Sametime => "sametime", Gateway::Signal => "signal", Gateway::SIMPLE => "simple", Gateway::Skype => "skype", Gateway::SMS => "sms", Gateway::SMTP => "smtp", Gateway::Steam => "steam", Gateway::Telegram => "telegram", Gateway::Tlen => "tlen", Gateway::Xfire => "xfire", Gateway::XMPP => "xmpp", Gateway::Yahoo => "yahoo", Gateway::Other(s) => s, } .to_owned() } } impl From<&str> for Gateway { fn from(value: &str) -> Self { match value { "aim" => Self::Aim, "discord" => Self::Discord, "facebook" => Self::Facebook, "gadu-gadu" => Self::GaduGadu, "http-ws" => Self::HttpWS, "icq" => Self::ICQ, "irc" => Self::IRC, "lcs" => Self::LCS, "mattermost" => Self::Mattermost, "mrim" => Self::MRIM, "msn" => Self::MSN, "myspaceim" => Self::MySpaceIM, "ocs" => Self::OCS, "pstn" => Self::PSTN, "qq" => Self::QQ, "sametime" => Self::Sametime, "signal" => Self::Signal, "simple" => Self::SIMPLE, "skype" => Self::Skype, "sms" => Self::SMS, "smtp" => Self::SMTP, "steam" => Self::Steam, "telegram" => Self::Telegram, "tlen" => Self::Tlen, "xfire" => Self::Xfire, "xmpp" => Self::XMPP, "yahoo" => Self::Yahoo, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Headline { NewMail, RSS, Weather, Other(String), } impl ToString for Headline { fn to_string(&self) -> String { match self { Headline::NewMail => "new-mail", Headline::RSS => "rss", Headline::Weather => "weather", Headline::Other(s) => s, } .to_string() } } impl From<&str> for Headline { fn from(value: &str) -> Self { match value { "new-mail" => Self::NewMail, "rss" => Self::RSS, "weather" => Self::Weather, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Hierarchy { Branch, Leaf, Other(String), } impl ToString for Hierarchy { fn to_string(&self) -> String { match self { Hierarchy::Branch => "branch", Hierarchy::Leaf => "leaf", Hierarchy::Other(s) => s, } .to_string() } } impl From<&str> for Hierarchy { fn from(value: &str) -> Self { match value { "branch" => Self::Branch, "leaf" => Self::Leaf, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Proxy { Bytestreams, Other(String), } impl ToString for Proxy { fn to_string(&self) -> String { match self { Proxy::Bytestreams => "bytestreams", Proxy::Other(s) => s, } .to_owned() } } impl From<&str> for Proxy { fn from(value: &str) -> Self { match value { "bytestreams" => Self::Bytestreams, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum PubSub { Collection, Leaf, PEP, Service, // TODO: should there be other in each of these Other(String), } impl ToString for PubSub { fn to_string(&self) -> String { match self { PubSub::Collection => "collection", PubSub::Leaf => "leaf", PubSub::PEP => "pep", PubSub::Service => "service", PubSub::Other(s) => s, } .to_owned() } } impl From<&str> for PubSub { fn from(value: &str) -> Self { match value { "collection" => Self::Collection, "leaf" => Self::Leaf, "pep" => Self::PEP, "service" => Self::Service, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Server { IM, Other(String), } impl ToString for Server { fn to_string(&self) -> String { match self { Server::IM => "im", Server::Other(s) => s, } .to_string() } } impl From<&str> for Server { fn from(value: &str) -> Self { match value { "im" => Self::IM, s => Self::Other(s.to_owned()), } } } #[derive(Debug, Clone)] pub enum Store { Berkeley, File, Generic, LDAP, MySQL, Oracle, Postgres, Other(String), } impl ToString for Store { fn to_string(&self) -> String { match self { Store::Berkeley => "berkeley", Store::File => "file", Store::Generic => "generic", Store::LDAP => "ldap", Store::MySQL => "mysql", Store::Oracle => "oracle", Store::Postgres => "postgres", Store::Other(s) => s, } .to_string() } } impl From<&str> for Store { fn from(value: &str) -> Self { match value { "berkeley" => Self::Berkeley, "file" => Self::File, "generic" => Self::Generic, "ldap" => Self::LDAP, "mysql" => Self::MySQL, "oracle" => Self::Oracle, "postgres" => Self::Postgres, s => Self::Other(s.to_owned()), } } } }