From 4454da15b600025c3b4f338313a8883d3d281df5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Jul 2017 17:33:58 +0100 Subject: [PATCH] disco: Implement disco#items. --- src/disco.rs | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 2 + 2 files changed, 162 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index 167440ed..aa816dec 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -7,6 +7,7 @@ use try_from::TryFrom; use minidom::Element; +use jid::Jid; use error::Error; use ns; @@ -176,9 +177,131 @@ impl From for Element { } } +#[derive(Debug, Clone)] +pub struct DiscoItemsQuery { + pub node: Option, +} + +impl TryFrom for DiscoItemsQuery { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not a disco#items element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in disco#items.")); + } + for (attr, _) in elem.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in disco#items.")); + } + } + Ok(DiscoItemsQuery { + node: get_attr!(elem, "node", optional), + }) + } +} + +impl From for Element { + fn from(disco: DiscoItemsQuery) -> Element { + Element::builder("query") + .ns(ns::DISCO_ITEMS) + .attr("node", disco.node) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct Item { + pub jid: Jid, + pub node: Option, + pub name: Option, +} + +impl TryFrom for Item { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("item", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not an item element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in item element.")); + } + for (attr, _) in elem.attrs() { + if attr != "jid" && attr != "node" && attr != "name" { + return Err(Error::ParseError("Unknown attribute in item element.")); + } + } + Ok(Item { + jid: get_attr!(elem, "jid", required), + node: get_attr!(elem, "node", optional), + name: get_attr!(elem, "name", optional), + }) + } +} + +impl From for Element { + fn from(item: Item) -> Element { + Element::builder("item") + .ns(ns::DISCO_ITEMS) + .attr("jid", String::from(item.jid)) + .attr("node", item.node) + .attr("name", item.name) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct DiscoItemsResult { + pub node: Option, + pub items: Vec, +} + +impl TryFrom for DiscoItemsResult { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not a disco#items element.")); + } + for (attr, _) in elem.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in disco#items.")); + } + } + + let mut items: Vec = vec!(); + for child in elem.children() { + if child.is("item", ns::DISCO_ITEMS) { + items.push(Item::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown element in disco#items.")); + } + } + + Ok(DiscoItemsResult { + node: get_attr!(elem, "node", optional), + items: items, + }) + } +} + +impl From for Element { + fn from(disco: DiscoItemsResult) -> Element { + Element::builder("query") + .ns(ns::DISCO_ITEMS) + .attr("node", disco.node) + .append(disco.items) + .build() + } +} + #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; #[test] fn test_simple() { @@ -273,4 +396,41 @@ mod tests { }; assert_eq!(message, "disco#info feature not present in disco#info."); } + + #[test] + fn test_simple_items() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsQuery::try_from(elem).unwrap(); + assert!(query.node.is_none()); + + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsQuery::try_from(elem).unwrap(); + assert_eq!(query.node, Some(String::from("coucou"))); + } + + #[test] + fn test_simple_items_result() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert!(query.node.is_none()); + assert!(query.items.is_empty()); + + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert_eq!(query.node, Some(String::from("coucou"))); + assert!(query.items.is_empty()); + } + + #[test] + fn test_answers_items_result() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert_eq!(query.items.len(), 2); + assert_eq!(query.items[0].jid, Jid::from_str("component").unwrap()); + assert_eq!(query.items[0].node, None); + assert_eq!(query.items[0].name, None); + assert_eq!(query.items[1].jid, Jid::from_str("component2").unwrap()); + assert_eq!(query.items[1].node, Some(String::from("test"))); + assert_eq!(query.items[1].name, Some(String::from("A component"))); + } } diff --git a/src/ns.rs b/src/ns.rs index 7edee1e4..0d06a1d7 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -18,6 +18,8 @@ pub const DATA_FORMS: &str = "jabber:x:data"; /// XEP-0030: Service Discovery pub const DISCO_INFO: &str = "http://jabber.org/protocol/disco#info"; +/// XEP-0030: Service Discovery +pub const DISCO_ITEMS: &str = "http://jabber.org/protocol/disco#items"; /// XEP-0045: Multi-User Chat pub const MUC: &str = "http://jabber.org/protocol/muc";