diff --git a/xmpp-parsers/src/pubsub/mod.rs b/xmpp-parsers/src/pubsub/mod.rs index e639c28a..5b15ef24 100644 --- a/xmpp-parsers/src/pubsub/mod.rs +++ b/xmpp-parsers/src/pubsub/mod.rs @@ -7,10 +7,14 @@ /// The `http://jabber.org/protocol/pubsub#event` protocol. pub mod event; +/// The `http://jabber.org/protocol/pubsub#owner` protocol. +pub mod owner; + /// The `http://jabber.org/protocol/pubsub` protocol. pub mod pubsub; pub use self::event::PubSubEvent; +pub use self::owner::PubSubOwner; pub use self::pubsub::PubSub; use crate::{Element, Jid}; diff --git a/xmpp-parsers/src/pubsub/owner.rs b/xmpp-parsers/src/pubsub/owner.rs new file mode 100644 index 00000000..b6d1d1c4 --- /dev/null +++ b/xmpp-parsers/src/pubsub/owner.rs @@ -0,0 +1,139 @@ +// Copyright (c) ??? +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::data_forms::DataForm; +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; +use crate::ns; +use crate::pubsub::NodeName; +use crate::util::error::Error; +use crate::Element; +use std::convert::TryFrom; + +generate_element!( + /// Request to configure a node. + Configure, "configure", PUBSUB_OWNER, + attributes: [ + /// The node to be configured. + node: Option = "node", + ], + children: [ + /// The form to configure it. + form: Option = ("x", DATA_FORMS) => DataForm + ] +); + +/// Main payload used to communicate with a PubSubOwner service. +/// +/// `` +#[derive(Debug, Clone)] +pub enum PubSubOwner { + /// Request to configure a node, with optional suggested name and suggested configuration. + Configure { + /// The configure request for the new node. + configure: Configure, + }, +} + +impl IqGetPayload for PubSubOwner {} +impl IqSetPayload for PubSubOwner {} +impl IqResultPayload for PubSubOwner {} + +impl TryFrom for PubSubOwner { + type Error = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "pubsub", PUBSUB_OWNER); + check_no_attributes!(elem, "pubsub"); + + let mut payload = None; + for child in elem.children() { + if child.is("configure", ns::PUBSUB_OWNER) { + if payload.is_some() { + return Err(Error::ParseError( + "Payload is already defined in pubsub owner element.", + )); + } + let configure = Configure::try_from(child.clone())?; + payload = Some(PubSubOwner::Configure { + configure: configure, + }); + } else { + return Err(Error::ParseError("Unknown child in pubsub element.")); + } + } + Ok(payload.ok_or(Error::ParseError("No payload in pubsub element."))?) + } +} + +impl From for Element { + fn from(pubsub: PubSubOwner) -> Element { + Element::builder("pubsub", ns::PUBSUB_OWNER) + .append_all(match pubsub { + PubSubOwner::Configure { configure } => vec![Element::from(configure)], + }) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; + use std::str::FromStr; + + #[test] + fn configure() { + // XXX: Do we want xmpp-parsers to always specify the field type in the output Element? + let elem: Element = "http://jabber.org/protocol/pubsub#node_configwhitelist" + .parse() + .unwrap(); + let elem1 = elem.clone(); + + let pubsub = PubSubOwner::Configure { + configure: Configure { + node: Some(NodeName(String::from("foo"))), + form: Some(DataForm { + type_: DataFormType::Submit, + form_type: Some(String::from(ns::PUBSUB_CONFIGURE)), + title: None, + instructions: None, + fields: vec![Field { + var: String::from("pubsub#access_model"), + type_: FieldType::ListSingle, + label: None, + required: false, + options: vec![], + values: vec![String::from("whitelist")], + media: vec![], + }], + }), + }, + }; + + let elem2 = Element::from(pubsub); + assert_eq!(elem1, elem2); + } + + #[test] + fn test_serialize_configure() { + let reference: Element = "" + .parse() + .unwrap(); + + let elem: Element = "".parse().unwrap(); + + let form = DataForm::try_from(elem).unwrap(); + + let configure = PubSubOwner::Configure { + configure: Configure { + node: Some(NodeName(String::from("foo"))), + form: Some(form), + }, + }; + let serialized: Element = configure.into(); + assert_eq!(serialized, reference); + } +}