diff options
Diffstat (limited to 'stanza/src/xep_0060/event.rs')
-rw-r--r-- | stanza/src/xep_0060/event.rs | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/stanza/src/xep_0060/event.rs b/stanza/src/xep_0060/event.rs new file mode 100644 index 0000000..bdd8b53 --- /dev/null +++ b/stanza/src/xep_0060/event.rs @@ -0,0 +1,450 @@ +use std::str::FromStr; + +use chrono::{DateTime, Utc}; +use jid::JID; +use peanuts::{ + element::{FromElement, IntoElement}, + DeserializeError, Element, +}; + +use crate::xep_0004::X; + +pub const XMLNS: &str = "http://jabber.org/protocol/pubsub#event"; + +#[derive(Clone, Debug)] +pub enum Event { + Collection(Collection), + Configuration(Configuration), + Delete(Delete), + Items(Items), + Purge(Purge), + Subscription(Subscription), +} + +impl FromElement for Event { + fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("event")?; + element.check_namespace(XMLNS)?; + + let child = element.pop_child_one::<Element>()?; + + match child.identify() { + (Some(XMLNS), "collection") => Ok(Self::Collection(Collection::from_element(child)?)), + (Some(XMLNS), "configuration") => { + Ok(Self::Configuration(Configuration::from_element(child)?)) + } + (Some(XMLNS), "delete") => Ok(Self::Delete(Delete::from_element(child)?)), + (Some(XMLNS), "items") => Ok(Self::Items(Items::from_element(child)?)), + (Some(XMLNS), "purge") => Ok(Self::Purge(Purge::from_element(child)?)), + (Some(XMLNS), "subscription") => { + Ok(Self::Subscription(Subscription::from_element(child)?)) + } + _ => Err(peanuts::DeserializeError::UnexpectedElement(child)), + } + } +} + +impl IntoElement for Event { + fn builder(&self) -> peanuts::element::ElementBuilder { + let builder = Element::builder("event", Some(XMLNS)); + + match self { + Event::Collection(collection) => builder.push_child(collection.clone()), + Event::Configuration(configuration) => builder.push_child(configuration.clone()), + Event::Delete(delete) => builder.push_child(delete.clone()), + Event::Items(items) => builder.push_child(items.clone()), + Event::Purge(purge) => builder.push_child(purge.clone()), + Event::Subscription(subscription) => builder.push_child(subscription.clone()), + } + } +} + +#[derive(Clone, Debug)] +pub struct Collection { + node: Option<String>, + r#type: CollectionType, +} + +impl FromElement for Collection { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("node")?; + element.check_namespace(XMLNS)?; + + let node = element.attribute_opt("node")?; + let r#type = element.pop_child_one()?; + + Ok(Self { node, r#type }) + } +} + +impl IntoElement for Collection { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("collection", Some(XMLNS)) + .push_attribute_opt("node", self.node.clone()) + .push_child(self.r#type.clone()) + } +} + +#[derive(Clone, Debug)] +pub enum CollectionType { + Associate(Associate), + Disassociate(Disassociate), +} + +impl FromElement for CollectionType { + fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> { + match element.identify() { + (Some(XMLNS), "associate") => { + Ok(CollectionType::Associate(Associate::from_element(element)?)) + } + (Some(XMLNS), "disassociate") => Ok(CollectionType::Disassociate( + Disassociate::from_element(element)?, + )), + _ => Err(DeserializeError::UnexpectedElement(element)), + } + } +} + +impl IntoElement for CollectionType { + fn builder(&self) -> peanuts::element::ElementBuilder { + match self { + CollectionType::Associate(associate) => associate.builder(), + CollectionType::Disassociate(disassociate) => disassociate.builder(), + } + } +} + +#[derive(Clone, Debug)] +pub struct Associate { + node: String, +} + +impl FromElement for Associate { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("associate")?; + element.check_namespace(XMLNS)?; + + let node = element.attribute("node")?; + + Ok(Self { node }) + } +} + +impl IntoElement for Associate { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("associate", Some(XMLNS)).push_attribute("node", self.node.clone()) + } +} + +#[derive(Clone, Debug)] +pub struct Disassociate { + node: String, +} + +impl FromElement for Disassociate { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("disassociate")?; + element.check_namespace(XMLNS)?; + + let node = element.attribute("node")?; + + Ok(Self { node }) + } +} + +impl IntoElement for Disassociate { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("disassociate", Some(XMLNS)).push_attribute("node", self.node.clone()) + } +} + +#[derive(Clone, Debug)] +pub struct Configuration { + node: Option<String>, + configuration: Option<X>, +} + +impl FromElement for Configuration { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("configuration")?; + element.check_namespace(XMLNS)?; + + let node = element.attribute_opt("node")?; + + let configuration = element.pop_child_opt()?; + + Ok(Self { + node, + configuration, + }) + } +} + +impl IntoElement for Configuration { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("configuration", Some(XMLNS)) + .push_attribute_opt("node", self.node.clone()) + .push_child_opt(self.configuration.clone()) + } +} + +#[derive(Clone, Debug)] +pub struct Delete { + node: String, + redirect: Option<Redirect>, +} + +impl FromElement for Delete { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("delete")?; + element.check_namespace(XMLNS)?; + + let node = element.attribute("node")?; + + let redirect = element.pop_child_opt()?; + + Ok(Self { node, redirect }) + } +} + +impl IntoElement for Delete { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("delete", Some(XMLNS)) + .push_attribute("node", self.node.clone()) + .push_child_opt(self.redirect.clone()) + } +} + +#[derive(Clone, Debug)] +pub struct Items { + node: String, + items: ItemsType, +} + +impl FromElement for Items { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("items")?; + element.check_namespace(XMLNS)?; + + let node = element.attribute("node")?; + + let items = if let Ok(items) = element.pop_children() { + ItemsType::Item(items) + } else { + ItemsType::Retract(element.pop_children()?) + }; + + Ok(Self { node, items }) + } +} + +impl IntoElement for Items { + fn builder(&self) -> peanuts::element::ElementBuilder { + let builder = + Element::builder("items", Some(XMLNS)).push_attribute("node", self.node.clone()); + + match &self.items { + ItemsType::Item(items) => builder.push_children(items.clone()), + ItemsType::Retract(retracts) => builder.push_children(retracts.clone()), + } + } +} + +#[derive(Clone, Debug)] +pub enum ItemsType { + Item(Vec<Item>), + Retract(Vec<Retract>), +} + +#[derive(Clone, Debug)] +pub struct Item { + id: Option<String>, + publisher: Option<String>, + item: Option<Content>, +} + +impl FromElement for Item { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("item")?; + element.check_namespace(XMLNS)?; + + let id = element.attribute_opt("id")?; + let publisher = element.attribute_opt("publisher")?; + + let item = element.pop_child_opt()?; + + Ok(Self { + id, + publisher, + item, + }) + } +} + +impl IntoElement for Item { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("item", Some(XMLNS)) + .push_attribute_opt("id", self.id.clone()) + .push_attribute_opt("publisher", self.publisher.clone()) + .push_child_opt(self.item.clone()) + } +} + +#[derive(Clone, Debug)] +pub enum Content { + Unknown, +} + +impl FromElement for Content { + fn from_element(_element: Element) -> peanuts::element::DeserializeResult<Self> { + // TODO: types + return Ok(Self::Unknown); + } +} + +impl IntoElement for Content { + fn builder(&self) -> peanuts::element::ElementBuilder { + panic!("unknown content cannot be serialized") + } +} + +#[derive(Clone, Debug)] +pub struct Purge { + node: String, +} + +impl FromElement for Purge { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("purge")?; + element.check_namespace(XMLNS)?; + + let node = element.attribute("node")?; + + Ok(Self { node }) + } +} + +impl IntoElement for Purge { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("purge", Some(XMLNS)).push_attribute("node", self.node.clone()) + } +} + +#[derive(Clone, Debug)] +pub struct Redirect { + uri: String, +} + +impl FromElement for Redirect { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("redirect")?; + element.check_namespace(XMLNS)?; + + let uri = element.attribute("uri")?; + + Ok(Self { uri }) + } +} + +impl IntoElement for Redirect { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("redirect", Some(XMLNS)).push_attribute("uri", self.uri.clone()) + } +} + +#[derive(Clone, Debug)] +pub struct Retract { + id: String, +} + +impl FromElement for Retract { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("retract")?; + element.check_namespace(XMLNS)?; + + let id = element.attribute("id")?; + + Ok(Self { id }) + } +} + +impl IntoElement for Retract { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("retract", Some(XMLNS)).push_attribute("id", self.id.clone()) + } +} + +#[derive(Clone, Debug)] +pub struct Subscription { + expiry: Option<DateTime<Utc>>, + jid: JID, + node: Option<String>, + subid: Option<String>, + subscription: Option<SubscriptionState>, +} + +impl FromElement for Subscription { + fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { + element.check_name("subscription")?; + element.check_namespace(XMLNS)?; + + let expiry = element.attribute_opt("expiry")?; + let jid = element.attribute("jid")?; + let node = element.attribute_opt("node")?; + let subid = element.attribute_opt("subid")?; + let subscription = element.attribute_opt("subscription")?; + + Ok(Self { + expiry, + jid, + node, + subid, + subscription, + }) + } +} + +impl IntoElement for Subscription { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("subscription", Some(XMLNS)) + .push_attribute_opt("expiry", self.expiry) + .push_attribute("jid", self.jid.clone()) + .push_attribute_opt("node", self.node.clone()) + .push_attribute_opt("subid", self.subid.clone()) + .push_attribute_opt("subscription", self.subscription.clone()) + } +} + +#[derive(Clone, Debug)] +pub enum SubscriptionState { + None, + Pending, + Subscribed, + Unconfigured, +} + +impl FromStr for SubscriptionState { + type Err = DeserializeError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "none" => Ok(Self::None), + "pending" => Ok(Self::Pending), + "subscribed" => Ok(Self::Subscribed), + "unconfigured" => Ok(Self::Unconfigured), + s => Err(DeserializeError::FromStr(s.to_owned())), + } + } +} + +impl ToString for SubscriptionState { + fn to_string(&self) -> String { + match self { + SubscriptionState::None => "none", + SubscriptionState::Pending => "pending", + SubscriptionState::Subscribed => "subscribed", + SubscriptionState::Unconfigured => "unconfigured", + } + .to_owned() + } +} |