From a625b88fce4e1eaeb9609a6f69393554ea1692f7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:29:51 +0200 Subject: [PATCH] macros: Merge all element children into a common syntax. --- src/jingle.rs | 79 +++-------------- src/macros.rs | 207 +++++++++++++++++++------------------------ src/mam.rs | 112 ++++++----------------- src/muc/muc.rs | 51 ++--------- src/muc/user.rs | 142 ++++++++--------------------- src/pubsub/pubsub.rs | 16 ++-- src/sm.rs | 4 +- src/version.rs | 79 +++-------------- 8 files changed, 200 insertions(+), 490 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index b64b0e7cccc0d73597b7f44d98ddb4f94e729cef..951a2fa1d72a570f9baa8f08c4029b1adb1ebe3f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -66,16 +66,20 @@ generate_attribute!(Disposition, "disposition", { generate_id!(ContentId); -#[derive(Debug, Clone)] -pub struct Content { - pub creator: Creator, - pub disposition: Disposition, - pub name: ContentId, - pub senders: Senders, - pub description: Option, - pub transport: Option, - pub security: Option, -} +generate_element_with_children!( + Content, "content", JINGLE, + attributes: [ + creator: Creator = "creator" => required, + disposition: Disposition = "disposition" => default, + name: ContentId = "name" => required, + senders: Senders = "senders" => default + ], + children: [ + description: Option = ("description", JINGLE) => Element, + transport: Option = ("transport", JINGLE) => Element, + security: Option = ("security", JINGLE) => Element + ] +); impl Content { pub fn new(creator: Creator, name: ContentId) -> Content { @@ -116,61 +120,6 @@ impl Content { } } -impl TryFrom for Content { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "content", JINGLE); - check_no_unknown_attributes!(elem, "content", ["creator", "disposition", "name", "senders"]); - - let mut content = Content { - creator: get_attr!(elem, "creator", required), - disposition: get_attr!(elem, "disposition", default), - name: get_attr!(elem, "name", required), - senders: get_attr!(elem, "senders", default), - description: None, - transport: None, - security: None, - }; - for child in elem.children() { - if child.name() == "description" { - if content.description.is_some() { - return Err(Error::ParseError("Content must not have more than one description.")); - } - content.description = Some(child.clone()); - } else if child.name() == "transport" { - if content.transport.is_some() { - return Err(Error::ParseError("Content must not have more than one transport.")); - } - content.transport = Some(child.clone()); - } else if child.name() == "security" { - if content.security.is_some() { - return Err(Error::ParseError("Content must not have more than one security.")); - } - content.security = Some(child.clone()); - } else { - return Err(Error::ParseError("Unknown child in content element.")); - } - } - Ok(content) - } -} - -impl From for Element { - fn from(content: Content) -> Element { - Element::builder("content") - .ns(ns::JINGLE) - .attr("creator", content.creator) - .attr("disposition", content.disposition) - .attr("name", content.name) - .attr("senders", content.senders) - .append(content.description) - .append(content.transport) - .append(content.security) - .build() - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), diff --git a/src/macros.rs b/src/macros.rs index df4b03e0a8817b3dc53b7df88177ca855b210d48..9adae5197760ab4c196add8d387f35e26ae60085 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -424,129 +424,96 @@ macro_rules! generate_element_with_text { ); } -macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); +macro_rules! start_decl { + (Vec, $type:ty) => ( + Vec<$type> ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + (Option, $type:ty) => ( + Option<$type> ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type, - )* - $( - $(#[$child_meta])* - pub $child_ident: Vec<$child_type>, - )* - } - - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { - check_self!(elem, $name, $ns); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_children = vec!(); - for child in elem.children() { - $( - if child.is($child_name, ::ns::$child_ns) { - let parsed_child = $child_constructor::try_from(child.clone())?; - parsed_children.push(parsed_child); - continue; - } - )* - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); - } - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action), - )* - $( - $child_ident: parsed_children, - )* - }) - } - } + (Required, $type:ty) => ( + $type + ); +} - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - $( - .append(elem.$child_ident) - )* - .build() - } - } +macro_rules! start_parse_elem { + ($temp:ident: Vec) => ( + let mut $temp = Vec::new(); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ($temp:ident: Option) => ( + let mut $temp = None; ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ($temp:ident: Required) => ( + let mut $temp = None; ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type, - )* - $(#[$child_meta])* - pub $child_ident: Option<$child_type>, - } +} - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; +macro_rules! do_parse { + ($elem:ident, Element) => ( + $elem.clone() + ); + ($elem:ident, String) => ( + $elem.text() + ); + ($elem:ident, $constructor:ident) => ( + $constructor::try_from($elem.clone())? + ); +} - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { - check_self!(elem, $name, $ns); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_child = None; - for child in elem.children() { - if child.is($child_name, ::ns::$child_ns) { - parsed_child = Some($child_constructor::try_from(child.clone())?); - continue; - } - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); - } - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action), - )* - $child_ident: parsed_child, - }) - } +macro_rules! do_parse_elem { + ($temp:ident: Vec = $constructor:ident => $elem:ident) => ( + $temp.push(do_parse!($elem, $constructor)); + ); + ($temp:ident: Option = $constructor:ident => $elem:ident) => ( + if $temp.is_some() { + return Err(::error::Error::ParseError(concat!("coucou", " must not have more than one ", "coucou", "."))); } + $temp = Some(do_parse!($elem, $constructor)); + ); + ($temp:ident: Required = $constructor:ident => $elem:ident) => ( + $temp = Some(do_parse!($elem, $constructor)); + ); +} - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - .append(elem.$child_ident) - .build() - } - } +macro_rules! finish_parse_elem { + ($temp:ident: Vec = $name:tt) => ( + $temp + ); + ($temp:ident: Option = $name:tt) => ( + $temp + ); + ($temp:ident: Required = $name:tt) => ( + $temp.ok_or(::error::Error::ParseError(concat!("Missing child coucou in ", $name, " element.")))? + ); +} + +macro_rules! generate_serialiser { + ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => ( + ::minidom::Element::builder($name) + .ns(::ns::$ns) + .append($parent.$elem) + .build() ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => ( + $parent.$elem.map(|elem| + ::minidom::Element::builder($name) + .ns(::ns::$ns) + .append(elem) + .build()) ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => ( + $parent.$elem ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( +} + +macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -554,8 +521,10 @@ macro_rules! generate_element_with_children { $(#[$attr_meta])* pub $attr: $attr_type, )* + $( $(#[$child_meta])* - pub $child_ident: $child_type, + pub $child_ident: start_decl!($coucou, $child_type), + )* } impl ::try_from::TryFrom<::minidom::Element> for $elem { @@ -564,19 +533,25 @@ macro_rules! generate_element_with_children { fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_child = None; + $( + start_parse_elem!($child_ident: $coucou); + )* for child in elem.children() { + $( if child.is($child_name, ::ns::$child_ns) { - parsed_child = Some($child_constructor::try_from(child.clone())?); + do_parse_elem!($child_ident: $coucou = $child_constructor => child); continue; } + )* return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( $attr: get_attr!(elem, $attr_name, $attr_action), )* - $child_ident: parsed_child.ok_or(::error::Error::ParseError(concat!("Missing child ", $child_name, " in ", $name, " element.")))?, + $( + $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name), + )* }) } } @@ -588,7 +563,9 @@ macro_rules! generate_element_with_children { $( .attr($attr_name, elem.$attr) )* - .append(elem.$child_ident) + $( + .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns))) + )* .build() } } diff --git a/src/mam.rs b/src/mam.rs index 664947ee49a2a1f72ab447d12bfedddd194d7c8b..b835fc505e688c56924979f2ff7f8656d63420b3 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -18,13 +18,17 @@ use forwarding::Forwarded; use ns; -#[derive(Debug, Clone)] -pub struct Query { - pub queryid: Option, - pub node: Option, - pub form: Option, - pub set: Option, -} +generate_element_with_children!( + Query, "query", MAM, + attributes: [ + queryid: Option = "queryid" => optional, + node: Option = "node" => optional + ], + children: [ + form: Option = ("x", DATA_FORMS) => DataForm, + set: Option = ("set", RSM) => Set + ] +); impl IqGetPayload for Query {} impl IqSetPayload for Query {} @@ -36,16 +40,24 @@ generate_element_with_children!( id: String = "id" => required, queryid: String = "queryid" => required, ], - child: ( - forwarded: Forwarded = ("forwarded", FORWARD) => Forwarded - ) + children: [ + forwarded: Required = ("forwarded", FORWARD) => Forwarded + ] ); -#[derive(Debug, Clone)] -pub struct Fin { - pub complete: bool, - pub set: Set, -} +generate_attribute!( + Complete, "complete", bool +); + +generate_element_with_children!( + Fin, "fin", MAM, + attributes: [ + complete: Complete = "complete" => default + ], + children: [ + set: Required = ("set", RSM) => Set + ] +); impl IqResultPayload for Fin {} @@ -66,54 +78,6 @@ impl IqGetPayload for Prefs {} impl IqSetPayload for Prefs {} impl IqResultPayload for Prefs {} -impl TryFrom for Query { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", MAM); - check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); - let mut form = None; - let mut set = None; - for child in elem.children() { - if child.is("x", ns::DATA_FORMS) { - form = Some(DataForm::try_from(child.clone())?); - } else if child.is("set", ns::RSM) { - set = Some(Set::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in query element.")); - } - } - let queryid = get_attr!(elem, "queryid", optional); - let node = get_attr!(elem, "node", optional); - Ok(Query { queryid, node, form, set }) - } -} - -impl TryFrom for Fin { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "fin", MAM); - check_no_unknown_attributes!(elem, "fin", ["complete"]); - let mut set = None; - for child in elem.children() { - if child.is("set", ns::RSM) { - set = Some(Set::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in fin element.")); - } - } - let set = set.ok_or(Error::ParseError("Mandatory set element missing in fin."))?; - let complete = match elem.attr("complete") { - Some(complete) if complete == "true" => true, - Some(complete) if complete == "false" => false, - None => false, - Some(_) => return Err(Error::ParseError("Invalid value for 'complete' attribute.")), - }; - Ok(Fin { complete, set }) - } -} - impl TryFrom for Prefs { type Err = Error; @@ -146,28 +110,6 @@ impl TryFrom for Prefs { } } -impl From for Element { - fn from(query: Query) -> Element { - Element::builder("query") - .ns(ns::MAM) - .attr("queryid", query.queryid) - .attr("node", query.node) - //.append(query.form) - .append(query.set) - .build() - } -} - -impl From for Element { - fn from(fin: Fin) -> Element { - Element::builder("fin") - .ns(ns::MAM) - .attr("complete", if fin.complete { Some("true") } else { None }) - .append(fin.set) - .build() - } -} - fn serialise_jid_list(name: &str, jids: Vec) -> Option { if jids.is_empty() { None diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 5132adbf6c1901b758680f9ae9ab343b921dd6c5..80905a20679928d1cf29f6a171b8fd88ee919a2c 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,53 +5,18 @@ // 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 try_from::TryFrom; - -use minidom::Element; - -use error::Error; - -use ns; - -#[derive(Debug, Clone)] -pub struct Muc { - pub password: Option, -} - -impl TryFrom for Muc { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "x", MUC); - check_no_attributes!(elem, "x"); - - let mut password = None; - for child in elem.children() { - if child.is("password", ns::MUC) { - password = Some(child.text()); - } else { - return Err(Error::ParseError("Unknown child in x element.")); - } - } - - Ok(Muc { - password: password, - }) - } -} - -impl From for Element { - fn from(muc: Muc) -> Element { - Element::builder("x") - .ns(ns::MUC) - .append(muc.password) - .build() - } -} +generate_element_with_children!( + Muc, "x", MUC, children: [ + password: Option = ("password", MUC) => String + ] +); #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; + use minidom::Element; + use error::Error; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/muc/user.rs b/src/muc/user.rs index f53799cdfc7370e9c5147a157a25b9c1d825ba73..69060cef781823e0a1457d09cca79ec291a32b57 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -5,7 +5,7 @@ // 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 try_from::{TryFrom, TryInto}; +use try_from::TryFrom; use minidom::Element; @@ -80,6 +80,7 @@ Status, "status", MUC_USER, "code", { /// Optional element used in elements inside presence stanzas of type /// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking /// purposes. -- CHANGELOG 0.17 (2002-10-23) +/// /// Possesses a 'jid' and a 'nick' attribute, so that an action can be attributed either to a real /// JID or to a roomnick. -- CHANGELOG 1.25 (2012-02-08) #[derive(Debug, Clone, PartialEq)] @@ -140,108 +141,25 @@ generate_attribute!(Role, "role", { None => "none", }, Default = None); -#[derive(Debug, Clone)] -pub struct Item { - pub affiliation: Affiliation, - pub jid: Option, - pub nick: Option, - pub role: Role, - pub actor: Option, - pub continue_: Option, - pub reason: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", MUC_USER); - check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); - let mut actor: Option = None; - let mut continue_: Option = None; - let mut reason: Option = None; - for child in elem.children() { - if child.is("actor", ns::MUC_USER) { - actor = Some(child.clone().try_into()?); - } else if child.is("continue", ns::MUC_USER) { - continue_ = Some(child.clone().try_into()?); - } else if child.is("reason", ns::MUC_USER) { - reason = Some(child.clone().try_into()?); - } else { - return Err(Error::ParseError("Unknown child in item element.")); - } - } - - let affiliation: Affiliation = get_attr!(elem, "affiliation", required); - let jid: Option = get_attr!(elem, "jid", optional); - let nick: Option = get_attr!(elem, "nick", optional); - let role: Role = get_attr!(elem, "role", required); - - Ok(Item{ - affiliation: affiliation, - jid: jid, - nick: nick, - role: role, - actor: actor, - continue_: continue_, - reason: reason, - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::MUC_USER) - .attr("affiliation", item.affiliation) - .attr("jid", item.jid) - .attr("nick", item.nick) - .attr("role", item.role) - .append(item.actor) - .append(item.continue_) - .append(item.reason) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct MucUser { - pub status: Vec, - pub items: Vec, -} - -impl TryFrom for MucUser { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "x", MUC_USER); - check_no_attributes!(elem, "x"); - let mut status = vec!(); - let mut items = vec!(); - for child in elem.children() { - if child.is("status", ns::MUC_USER) { - status.push(Status::try_from(child.clone())?); - } else if child.is("item", ns::MUC_USER) { - items.push(Item::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in x element.")); - } - } - Ok(MucUser { - status, - items, - }) - } -} - -impl From for Element { - fn from(muc_user: MucUser) -> Element { - Element::builder("x") - .ns(ns::MUC_USER) - .append(muc_user.status) - .build() - } -} +generate_element_with_children!( + Item, "item", MUC_USER, attributes: [ + affiliation: Affiliation = "affiliation" => required, + jid: Option = "jid" => optional, + nick: Option = "nick" => optional, + role: Role = "role" => required + ], children: [ + actor: Option = ("actor", MUC_USER) => Actor, + continue_: Option = ("continue", MUC_USER) => Continue, + reason: Option = ("reason", MUC_USER) => Reason + ] +); + +generate_element_with_children!( + MucUser, "x", MUC_USER, children: [ + status: Vec = ("status", MUC_USER) => Status, + items: Vec = ("item", MUC_USER) => Item + ] +); #[cfg(test)] mod tests { @@ -257,6 +175,24 @@ mod tests { MucUser::try_from(elem).unwrap(); } + #[test] + fn statuses_and_items() { + let elem: Element = " + + + + + + ".parse().unwrap(); + let muc_user = MucUser::try_from(elem).unwrap(); + assert_eq!(muc_user.status.len(), 2); + assert_eq!(muc_user.status[0], Status::AffiliationChange); + assert_eq!(muc_user.status[1], Status::ConfigShowsUnavailableMembers); + assert_eq!(muc_user.items.len(), 1); + assert_eq!(muc_user.items[0].affiliation, Affiliation::Member); + assert_eq!(muc_user.items[0].role, Role::Moderator); + } + #[test] fn test_invalid_child() { let elem: Element = " diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 569a954a7fe7754d2de345407f29e9921d98a90e..840232b0f2c15c804cd343e77ac0b0a4af3b754e 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -72,10 +72,10 @@ generate_element_with_only_attributes!( generate_element_with_children!( /// Request to configure a new node. Configure, "configure", PUBSUB, - child: ( + children: [ /// The form to configure it. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_element_with_only_attributes!( @@ -168,10 +168,10 @@ generate_element_with_children!( /// The subscription identifier affected by this request. subid: Option = "subid" => optional, ], - child: ( + children: [ /// The form describing the subscription. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_element_with_children!( @@ -190,10 +190,10 @@ generate_element_with_children!( generate_element_with_children!( /// The options associated to a publish request. PublishOptions, "publish-options", PUBSUB, - child: ( + children: [ /// The form describing these options. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_attribute!( @@ -300,10 +300,10 @@ generate_element_with_children!( /// The state of the subscription. subscription: Option = "subscription" => optional, ], - child: ( + children: [ /// The options related to this subscription. subscribe_options: Option = ("subscribe-options", PUBSUB) => SubscribeOptions - ) + ] ); generate_element_with_only_attributes!( diff --git a/src/sm.rs b/src/sm.rs index 994c8da7fde17c76276b10ac48f6cd696df1f8c9..4030bf71c3eabb919e4b17aca796b6e2a97632e7 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -62,10 +62,10 @@ generate_element_with_children!( attributes: [ h: Option = "h" => optional, ], - child: ( + children: [ // XXX: implement the * handling. error: Option = ("*", XMPP_STANZAS) => DefinedCondition - ) + ] ); generate_empty_element!( diff --git a/src/version.rs b/src/version.rs index cb8773369a6f4ef41d69cca186b36859e5ddff90..94cfcbb28c95f08c42420291983e220545e4e800 100644 --- a/src/version.rs +++ b/src/version.rs @@ -4,84 +4,25 @@ // 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 try_from::TryFrom; -use minidom::Element; -use error::Error; -use ns; use iq::{IqGetPayload, IqResultPayload}; -#[derive(Debug, Clone)] -pub struct Version { - pub name: String, - pub version: String, - pub os: Option, -} +generate_element_with_children!( + Version, "query", VERSION, + children: [ + name: Required = ("name", VERSION) => String, + version: Required = ("version", VERSION) => String, + os: Option = ("os", VERSION) => String + ] +); impl IqGetPayload for Version {} impl IqResultPayload for Version {} -impl TryFrom for Version { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", VERSION, "version"); - check_no_attributes!(elem, "version"); - let mut name = None; - let mut version = None; - let mut os = None; - for child in elem.children() { - if child.is("name", ns::VERSION) { - if name.is_some() { - return Err(Error::ParseError("More than one name in version element.")); - } - name = Some(child.text()); - } else if child.is("version", ns::VERSION) { - if version.is_some() { - return Err(Error::ParseError("More than one version in version element.")); - } - version = Some(child.text()); - } else if child.is("os", ns::VERSION) { - if os.is_some() { - return Err(Error::ParseError("More than one os in version element.")); - } - os = Some(child.text()); - } else { - return Err(Error::ParseError("Unknown child in version element.")); - } - } - let name = name.unwrap(); - let version = version.unwrap(); - Ok(Version { - name, - version, - os, - }) - } -} - -impl From for Element { - fn from(version: Version) -> Element { - Element::builder("query") - .ns(ns::VERSION) - .append(Element::builder("name") - .ns(ns::VERSION) - .append(version.name) - .build()) - .append(Element::builder("version") - .ns(ns::VERSION) - .append(version.version) - .build()) - .append(Element::builder("os") - .ns(ns::VERSION) - .append(version.os) - .build()) - .build() - } -} - #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; + use minidom::Element; use compare_elements::NamespaceAwareCompare; #[test]