use std::str::FromStr; use jid::JID; use peanuts::{ element::{FromElement, IntoElement}, DeserializeError, Element, }; use crate::xep_0004::X; pub const XMLNS: &str = "http://jabber.org/protocol/pubsub"; #[derive(Clone, Debug)] pub enum Pubsub { Create(Create, Option), Subscribe(Option, Option), Publish(Publish, Option), Affiliations(Affiliations), Default(Default), Items(Items), Retract(Retract), Subscription(Subscription), Subscriptions(Subscriptions), Unsubscribe(Unsubscribe), Pubsub, } impl FromElement for Pubsub { fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult { element.check_name("pubsub")?; element.check_namespace(XMLNS)?; let first_child = element.pop_child_opt::()?; if let Some(first_child) = first_child { match first_child.identify() { (Some(XMLNS), "create") => { let create = Create::from_element(first_child)?; let configure = element.pop_child_opt()?; Ok(Self::Create(create, configure)) } (Some(XMLNS), "subscribe") => { let subscribe = Subscribe::from_element(first_child)?; let options = element.pop_child_opt()?; Ok(Self::Subscribe(Some(subscribe), options)) } (Some(XMLNS), "options") => { let options = Options::from_element(first_child)?; Ok(Self::Subscribe(None, Some(options))) } (Some(XMLNS), "publish") => { let publish = Publish::from_element(first_child)?; let publish_options = element.pop_child_opt()?; Ok(Self::Publish(publish, publish_options)) } (Some(XMLNS), "affiliations") => { Ok(Self::Affiliations(Affiliations::from_element(first_child)?)) } (Some(XMLNS), "default") => Ok(Self::Default(Default::from_element(first_child)?)), (Some(XMLNS), "items") => Ok(Self::Items(Items::from_element(first_child)?)), (Some(XMLNS), "retract") => Ok(Self::Retract(Retract::from_element(first_child)?)), (Some(XMLNS), "subscription") => { Ok(Self::Subscription(Subscription::from_element(first_child)?)) } (Some(XMLNS), "subscriptions") => Ok(Self::Subscriptions( Subscriptions::from_element(first_child)?, )), (Some(XMLNS), "unsubscribe") => { Ok(Self::Unsubscribe(Unsubscribe::from_element(first_child)?)) } _ => Err(peanuts::DeserializeError::UnexpectedElement(first_child)), } } else { Ok(Pubsub::Pubsub) } } } impl IntoElement for Pubsub { fn builder(&self) -> peanuts::element::ElementBuilder { let element = Element::builder("pubsub", Some(XMLNS)); match self { Pubsub::Create(create, configure) => element .push_child(create.clone()) .push_child_opt(configure.clone()), Pubsub::Subscribe(subscribe, options) => element .push_child_opt(subscribe.clone()) .push_child_opt(options.clone()), Pubsub::Publish(publish, publish_options) => element .push_child(publish.clone()) .push_child_opt(publish_options.clone()), Pubsub::Affiliations(affiliations) => element.push_child(affiliations.clone()), Pubsub::Default(default) => element.push_child(default.clone()), Pubsub::Items(items) => element.push_child(items.clone()), Pubsub::Retract(retract) => element.push_child(retract.clone()), Pubsub::Subscription(subscription) => element.push_child(subscription.clone()), Pubsub::Subscriptions(subscriptions) => element.push_child(subscriptions.clone()), Pubsub::Unsubscribe(unsubscribe) => element.push_child(unsubscribe.clone()), Pubsub::Pubsub => element, } } } #[derive(Clone, Debug)] pub struct Affiliations { node: Option, affiliations: Vec, } impl FromElement for Affiliations { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("affiliations")?; element.check_namespace(XMLNS)?; let node = element.attribute_opt("node")?; let affiliations = element.pop_children()?; Ok(Self { node, affiliations }) } } impl IntoElement for Affiliations { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("affilations", Some(XMLNS)) .push_attribute_opt("node", self.node.clone()) .push_children(self.affiliations.clone()) } } // empty #[derive(Clone, Debug)] pub struct Affiliation { affiliation: AffiliationType, node: String, } impl FromElement for Affiliation { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("affiliation")?; element.check_namespace(XMLNS)?; let affiliation = element.attribute("affiliation")?; let node = element.attribute("node")?; Ok(Self { affiliation, node }) } } impl IntoElement for Affiliation { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("affiliation", Some(XMLNS)) .push_attribute("affiliation", self.affiliation.clone()) .push_attribute("node", self.node.clone()) } } #[derive(Clone, Debug)] pub enum AffiliationType { Member, None, Outcast, Owner, Publisher, PublishOnly, } impl FromStr for AffiliationType { type Err = DeserializeError; fn from_str(s: &str) -> Result { match s { "member" => Ok(AffiliationType::Member), "none" => Ok(AffiliationType::None), "outcast" => Ok(AffiliationType::Outcast), "owner" => Ok(AffiliationType::Owner), "publisher" => Ok(AffiliationType::Publisher), "publish-only" => Ok(AffiliationType::PublishOnly), s => Err(DeserializeError::FromStr(s.to_string())), } } } impl ToString for AffiliationType { fn to_string(&self) -> String { match self { AffiliationType::Member => "member", AffiliationType::None => "none", AffiliationType::Outcast => "outcast", AffiliationType::Owner => "owner", AffiliationType::Publisher => "publisher", AffiliationType::PublishOnly => "publish-only", } .to_owned() } } #[derive(Clone, Debug)] pub struct Configure(Option); impl FromElement for Configure { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("configure")?; element.check_namespace(XMLNS)?; Ok(Configure(element.pop_child_opt()?)) } } impl IntoElement for Configure { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("configure", Some(XMLNS)).push_child_opt(self.0.clone()) } } #[derive(Clone, Debug)] pub struct Create { node: Option, } impl FromElement for Create { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("create")?; element.check_namespace(XMLNS)?; let node = element.attribute_opt("node")?; Ok(Self { node }) } } impl IntoElement for Create { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("create", Some(XMLNS)).push_attribute_opt("node", self.node.clone()) } } #[derive(Clone, Debug)] pub struct Default { node: Option, // default leaf r#type: Option, default: Option, } impl FromElement for Default { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("default")?; element.check_namespace(XMLNS)?; let node = element.attribute_opt("node")?; let r#type = element.attribute_opt("type")?; let default = element.pop_child_opt()?; Ok(Self { node, r#type, default, }) } } impl IntoElement for Default { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("default", Some(XMLNS)) .push_attribute_opt("node", self.node.clone()) .push_attribute_opt("type", self.r#type.clone()) .push_child_opt(self.default.clone()) } } #[derive(Clone, Debug)] pub enum DefaultType { Collection, Leaf, } impl FromStr for DefaultType { type Err = DeserializeError; fn from_str(s: &str) -> Result { match s { "collection" => Ok(Self::Collection), "leaf" => Ok(Self::Leaf), s => Err(DeserializeError::FromStr(s.to_string())), } } } impl ToString for DefaultType { fn to_string(&self) -> String { match self { DefaultType::Collection => "collection", DefaultType::Leaf => "leaf", } .to_owned() } } #[derive(Clone, Debug)] pub struct Items { max_items: Option, node: String, subid: Option, items: Vec, } impl FromElement for Items { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("items")?; element.check_namespace(XMLNS)?; let max_items = element.attribute_opt("max_items")?; let node = element.attribute("node")?; let subid = element.attribute_opt("subid")?; let items = element.pop_children()?; Ok(Self { max_items, node, subid, items, }) } } impl IntoElement for Items { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("items", Some(XMLNS)) .push_attribute_opt("max_items", self.max_items) .push_attribute("node", self.node.clone()) .push_attribute_opt("subid", self.subid.clone()) .push_children(self.items.clone()) } } #[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 { Unknown, } impl FromElement for Content { fn from_element(_element: Element) -> peanuts::element::DeserializeResult { // 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 Options { jid: JID, node: Option, subid: Option, options: Option, } impl FromElement for Options { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("options")?; element.check_namespace(XMLNS)?; let jid = element.attribute("jid")?; let node = element.attribute_opt("node")?; let subid = element.attribute_opt("subid")?; let options = element.pop_child_opt()?; Ok(Self { jid, node, subid, options, }) } } impl IntoElement for Options { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("options", Some(XMLNS)) .push_attribute("jid", self.jid.clone()) .push_attribute_opt("node", self.node.clone()) .push_attribute_opt("subid", self.subid.clone()) .push_child_opt(self.options.clone()) } } #[derive(Clone, Debug)] pub struct Publish { node: String, items: Vec, } impl FromElement for Publish { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("publish")?; element.check_namespace(XMLNS)?; let node = element.attribute("node")?; let items = element.pop_children()?; Ok(Self { node, items }) } } impl IntoElement for Publish { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("publish", Some(XMLNS)) .push_attribute("node", self.node.clone()) .push_children(self.items.clone()) } } #[derive(Clone, Debug)] pub struct PublishOptions(pub Option); impl FromElement for PublishOptions { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("publish-options")?; element.check_namespace(XMLNS)?; Ok(Self(element.pop_child_opt()?)) } } impl IntoElement for PublishOptions { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("publish-options", Some(XMLNS)).push_child_opt(self.0.clone()) } } #[derive(Clone, Debug)] pub struct Retract { node: String, notify: Option, /// minOccurs = 1 items: Vec, } impl FromElement for Retract { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("retract")?; element.check_namespace(XMLNS)?; let node = element.attribute("node")?; let notify = element.attribute_opt("notify")?; let items = element.pop_children()?; Ok(Self { node, notify, items, }) } } impl IntoElement for Retract { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("retract", Some(XMLNS)) .push_attribute("node", self.node.clone()) .push_attribute_opt("notify", self.notify) .push_children(self.items.clone()) } } #[derive(Clone, Debug)] pub struct SubscribeOptions { // element required: bool, } impl FromElement for SubscribeOptions { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("subscribe-options")?; element.check_namespace(XMLNS)?; let required; if let Some(_) = element.pop_child_opt::()? { required = true; } else { required = false; } Ok(Self { required }) } } impl IntoElement for SubscribeOptions { fn builder(&self) -> peanuts::element::ElementBuilder { let builder = Element::builder("subscribe-options", Some(XMLNS)); if self.required { builder.push_child(Required) } else { builder } } } pub struct Required; impl FromElement for Required { fn from_element(element: Element) -> peanuts::element::DeserializeResult { element.check_name("required")?; element.check_namespace(XMLNS)?; Ok(Self) } } impl IntoElement for Required { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("required", Some(XMLNS)) } } #[derive(Clone, Debug)] pub struct Subscribe { jid: JID, node: Option, } impl FromElement for Subscribe { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("subscribe")?; element.check_namespace(XMLNS)?; let jid = element.attribute("jid")?; let node = element.attribute_opt("node")?; Ok(Self { jid, node }) } } impl IntoElement for Subscribe { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("subscribe", Some(XMLNS)) .push_attribute("jid", self.jid.clone()) .push_attribute_opt("node", self.node.clone()) } } #[derive(Clone, Debug)] pub struct Subscriptions { node: Option, subscriptions: Vec, } impl FromElement for Subscriptions { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("subscriptions")?; element.check_namespace(XMLNS)?; let node = element.attribute_opt("node")?; let subscriptions = element.pop_children()?; Ok(Self { node, subscriptions, }) } } impl IntoElement for Subscriptions { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("subscriptions", Some(XMLNS)) .push_attribute_opt("node", self.node.clone()) .push_children(self.subscriptions.clone()) } } #[derive(Clone, Debug)] pub struct Subscription { jid: JID, node: Option, subid: Option, subscription: Option, subscribe_options: Option, } impl FromElement for Subscription { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("subscription")?; element.check_namespace(XMLNS)?; let jid = element.attribute("jid")?; let node = element.attribute_opt("node")?; let subid = element.attribute_opt("subid")?; let subscription = element.attribute_opt("subscription")?; let subscribe_options = element.pop_child_opt()?; Ok(Self { jid, node, subid, subscription, subscribe_options, }) } } impl IntoElement for Subscription { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("subscription", Some(XMLNS)) .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()) .push_child_opt(self.subscribe_options.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_string() } } #[derive(Clone, Debug)] pub struct Unsubscribe { jid: JID, node: Option, subid: Option, } impl FromElement for Unsubscribe { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult { element.check_name("unsubscribe")?; element.check_namespace(XMLNS)?; let jid = element.attribute("jid")?; let node = element.attribute_opt("node")?; let subid = element.attribute_opt("subid")?; Ok(Self { jid, node, subid }) } } impl IntoElement for Unsubscribe { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("unsubscribe", Some(XMLNS)) .push_attribute("jid", self.jid.clone()) .push_attribute_opt("node", self.node.clone()) .push_attribute_opt("subid", self.subid.clone()) } } // TODO: enforce 'empty'