pubsub: Make Item common to both pubsub and pubsub#event namespaces.

Emmanuel Gil Peyrot created

Change summary

src/pubsub/event.rs  | 58 +++------------------------------------------
src/pubsub/mod.rs    | 26 ++++++++++++++++++++
src/pubsub/pubsub.rs | 42 +++------------------------------
src/util/macros.rs   | 51 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 85 insertions(+), 92 deletions(-)

Detailed changes

src/pubsub/event.rs 🔗

@@ -8,66 +8,16 @@ use crate::data_forms::DataForm;
 use crate::date::DateTime;
 use crate::util::error::Error;
 use crate::ns;
-use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, PubSubPayload};
+use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem};
 use jid::Jid;
 use minidom::Element;
 use try_from::TryFrom;
 
-/// One PubSub item from a node.
+/// Event wrapper for a PubSub `<item/>`.
 #[derive(Debug, Clone)]
-pub struct Item {
-    /// The identifier for this item, unique per node.
-    pub id: Option<ItemId>,
+pub struct Item(pub PubSubItem);
 
-    /// The JID of the entity who published this item.
-    pub publisher: Option<Jid>,
-
-    /// The actual content of this item.
-    pub payload: Option<Element>,
-}
-
-impl TryFrom<Element> for Item {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<Item, Error> {
-        check_self!(elem, "item", PUBSUB_EVENT);
-        check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
-        let mut payloads = elem.children().cloned().collect::<Vec<_>>();
-        let payload = payloads.pop();
-        if !payloads.is_empty() {
-            return Err(Error::ParseError(
-                "More than a single payload in item element.",
-            ));
-        }
-        Ok(Item {
-            payload,
-            id: get_attr!(elem, "id", optional),
-            publisher: get_attr!(elem, "publisher", optional),
-        })
-    }
-}
-
-impl From<Item> for Element {
-    fn from(item: Item) -> Element {
-        Element::builder("item")
-            .ns(ns::PUBSUB_EVENT)
-            .attr("id", item.id)
-            .attr("publisher", item.publisher)
-            .append(item.payload)
-            .build()
-    }
-}
-
-impl Item {
-    /// Create a new item event, accepting only payloads implementing `PubSubPayload`.
-    pub fn new<P: PubSubPayload>(id: Option<ItemId>, publisher: Option<Jid>, payload: Option<P>) -> Item {
-        Item {
-            id,
-            publisher,
-            payload: payload.map(|payload| payload.into()),
-        }
-    }
-}
+impl_pubsub_item!(Item, PUBSUB_EVENT);
 
 /// Represents an event happening to a PubSub node.
 #[derive(Debug, Clone)]

src/pubsub/mod.rs 🔗

@@ -13,6 +13,8 @@ pub mod pubsub;
 pub use self::event::PubSubEvent;
 pub use self::pubsub::PubSub;
 
+use crate::{Jid, Element};
+
 generate_id!(
     /// The name of a PubSub node, used to identify it on a JID.
     NodeName
@@ -46,5 +48,29 @@ generate_attribute!(
     }, Default = None
 );
 
+/// An item from a PubSub node.
+#[derive(Debug, Clone)]
+pub struct Item {
+    /// The identifier for this item, unique per node.
+    pub id: Option<ItemId>,
+
+    /// The JID of the entity who published this item.
+    pub publisher: Option<Jid>,
+
+    /// The payload of this item, in an arbitrary namespace.
+    pub payload: Option<Element>,
+}
+
+impl Item {
+    /// Create a new item, accepting only payloads implementing `PubSubPayload`.
+    pub fn new<P: PubSubPayload>(id: Option<ItemId>, publisher: Option<Jid>, payload: Option<P>) -> Item {
+        Item {
+            id,
+            publisher,
+            payload: payload.map(|payload| payload.into()),
+        }
+    }
+}
+
 /// This trait should be implemented on any element which can be included as a PubSub payload.
 pub trait PubSubPayload: crate::TryFrom<crate::Element> + Into<crate::Element> {}

src/pubsub/pubsub.rs 🔗

@@ -8,7 +8,7 @@ use crate::data_forms::DataForm;
 use crate::util::error::Error;
 use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
 use crate::ns;
-use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId};
+use crate::pubsub::{NodeName, Subscription, SubscriptionId, Item as PubSubItem};
 use jid::Jid;
 use minidom::Element;
 use try_from::TryFrom;
@@ -113,45 +113,11 @@ generate_element!(
     ]
 );
 
-/// An item from a PubSub node.
+/// Response wrapper for a PubSub `<item/>`.
 #[derive(Debug, Clone)]
-pub struct Item {
-    /// The payload of this item, in an arbitrary namespace.
-    pub payload: Option<Element>,
+pub struct Item(pub PubSubItem);
 
-    /// The 'id' attribute of this item.
-    pub id: Option<ItemId>,
-}
-
-impl TryFrom<Element> for Item {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<Item, Error> {
-        check_self!(elem, "item", PUBSUB);
-        check_no_unknown_attributes!(elem, "item", ["id"]);
-        let mut payloads = elem.children().cloned().collect::<Vec<_>>();
-        let payload = payloads.pop();
-        if !payloads.is_empty() {
-            return Err(Error::ParseError(
-                "More than a single payload in item element.",
-            ));
-        }
-        Ok(Item {
-            payload,
-            id: get_attr!(elem, "id", optional),
-        })
-    }
-}
-
-impl From<Item> for Element {
-    fn from(item: Item) -> Element {
-        Element::builder("item")
-            .ns(ns::PUBSUB)
-            .attr("id", item.id)
-            .append(item.payload)
-            .build()
-    }
-}
+impl_pubsub_item!(Item, PUBSUB);
 
 generate_element!(
     /// The options associated to a subscription request.

src/util/macros.rs 🔗

@@ -596,3 +596,54 @@ macro_rules! assert_size (
         assert_eq!(::std::mem::size_of::<$t>(), $sz);
     );
 );
+
+// TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there.
+macro_rules! impl_pubsub_item {
+    ($item:ident, $ns:ident) => {
+        impl crate::TryFrom<crate::Element> for $item {
+            type Err = Error;
+
+            fn try_from(elem: crate::Element) -> Result<$item, Error> {
+                check_self!(elem, "item", $ns);
+                check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
+                let mut payloads = elem.children().cloned().collect::<Vec<_>>();
+                let payload = payloads.pop();
+                if !payloads.is_empty() {
+                    return Err(Error::ParseError(
+                        "More than a single payload in item element.",
+                    ));
+                }
+                Ok($item(crate::pubsub::Item {
+                    id: get_attr!(elem, "id", optional),
+                    publisher: get_attr!(elem, "publisher", optional),
+                    payload,
+                }))
+            }
+        }
+
+        impl From<$item> for crate::Element {
+            fn from(item: $item) -> crate::Element {
+                crate::Element::builder("item")
+                    .ns(ns::$ns)
+                    .attr("id", item.0.id)
+                    .attr("publisher", item.0.publisher)
+                    .append(item.0.payload)
+                    .build()
+            }
+        }
+
+        impl ::std::ops::Deref for $item {
+            type Target = crate::pubsub::Item;
+
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
+
+        impl ::std::ops::DerefMut for $item {
+            fn deref_mut(&mut self) -> &mut Self::Target {
+                &mut self.0
+            }
+        }
+    }
+}