use std::str::FromStr; use chrono::{DateTime, Utc}; use jid::JID; use peanuts::{ element::{FromElement, IntoElement}, DeserializeError, Element, }; use crate::xep_0004::X; #[cfg(feature = "xep_0172")] use crate::xep_0172::{self, Nick}; 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 { element.check_name("event")?; element.check_namespace(XMLNS)?; let child = element.pop_child_one::()?; 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, r#type: CollectionType, } impl FromElement for Collection { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { 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 { 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 { 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 { 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, configuration: Option, } impl FromElement for Configuration { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { 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, } impl FromElement for Delete { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { 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 { 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), Retract(Vec), } #[derive(Clone, Debug)] pub struct Item { id: Option, publisher: Option, item: Option, } impl FromElement for Item { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { 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 { #[cfg(feature = "xep_0172")] Nick(Nick), Unknown(Element), } impl FromElement for Content { fn from_element(element: Element) -> peanuts::element::DeserializeResult { match element.identify() { #[cfg(feature = "xep_0172")] (Some(xep_0172::XMLNS), "nick") => Ok(Content::Nick(Nick::from_element(element)?)), _ => Ok(Self::Unknown(element)), } } } impl IntoElement for Content { fn builder(&self) -> peanuts::element::ElementBuilder { match self { #[cfg(feature = "xep_0172")] Content::Nick(nick) => nick.builder(), Content::Unknown(_e) => 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 { 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 { 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 { 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>, jid: JID, node: Option, subid: Option, subscription: Option, } impl FromElement for Subscription { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { 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 { 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() } }