roster.rs

  1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7#![deny(missing_docs)]
  8
  9use jid::Jid;
 10use iq::{IqGetPayload, IqSetPayload, IqResultPayload};
 11
 12generate_elem_id!(
 13    /// Represents a group a contact is part of.
 14    Group, "group", ROSTER
 15);
 16
 17generate_attribute!(
 18    /// The state of your mutual subscription with a contact.
 19    Subscription, "subscription", {
 20        /// The user doesn’t have any subscription to this contact’s presence,
 21        /// and neither does this contact.
 22        None => "none",
 23
 24        /// Only this contact has a subscription with you, not the opposite.
 25        From => "from",
 26
 27        /// Only you have a subscription with this contact, not the opposite.
 28        To => "to",
 29
 30        /// Both you and your contact are subscribed to each other’s presence.
 31        Both => "both",
 32
 33        /// In a roster set, this asks the server to remove this contact item
 34        /// from your roster.
 35        Remove => "remove",
 36    }, Default = None
 37);
 38
 39generate_element!(
 40    /// Contact from the user’s contact list.
 41    #[derive(PartialEq)]
 42    Item, "item", ROSTER,
 43    attributes: [
 44        /// JID of this contact.
 45        jid: Jid = "jid" => required,
 46
 47        /// Name of this contact.
 48        name: Option<String> = "name" => optional_empty,
 49
 50        /// Subscription status of this contact.
 51        subscription: Subscription = "subscription" => default
 52    ],
 53
 54    children: [
 55        /// Groups this contact is part of.
 56        groups: Vec<Group> = ("group", ROSTER) => Group
 57    ]
 58);
 59
 60generate_element!(
 61    /// The contact list of the user.
 62    Roster, "query", ROSTER,
 63    attributes: [
 64        /// Version of the contact list.
 65        ///
 66        /// This is an opaque string that should only be sent back to the server on
 67        /// a new connection, if this client is storing the contact list between
 68        /// connections.
 69        ver: Option<String> = "ver" => optional
 70    ],
 71    children: [
 72        /// List of the contacts of the user.
 73        items: Vec<Item> = ("item", ROSTER) => Item
 74    ]
 75);
 76
 77impl IqGetPayload for Roster {}
 78impl IqSetPayload for Roster {}
 79impl IqResultPayload for Roster {}
 80
 81#[cfg(test)]
 82mod tests {
 83    use super::*;
 84    use try_from::TryFrom;
 85    use minidom::Element;
 86    use error::Error;
 87    use std::str::FromStr;
 88    use compare_elements::NamespaceAwareCompare;
 89
 90    #[test]
 91    fn test_get() {
 92        let elem: Element = "<query xmlns='jabber:iq:roster'/>".parse().unwrap();
 93        let roster = Roster::try_from(elem).unwrap();
 94        assert!(roster.ver.is_none());
 95        assert!(roster.items.is_empty());
 96    }
 97
 98    #[test]
 99    fn test_result() {
100        let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver7'><item jid='nurse@example.com'/><item jid='romeo@example.net'/></query>".parse().unwrap();
101        let roster = Roster::try_from(elem).unwrap();
102        assert_eq!(roster.ver, Some(String::from("ver7")));
103        assert_eq!(roster.items.len(), 2);
104
105        let elem2: Element = "<query xmlns='jabber:iq:roster' ver='ver7'><item jid='nurse@example.com'/><item jid='romeo@example.net' name=''/></query>".parse().unwrap();
106        let roster2 = Roster::try_from(elem2).unwrap();
107        assert_eq!(roster.items, roster2.items);
108
109        let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver9'/>".parse().unwrap();
110        let roster = Roster::try_from(elem).unwrap();
111        assert_eq!(roster.ver, Some(String::from("ver9")));
112        assert!(roster.items.is_empty());
113
114        let elem: Element = r#"
115<query xmlns='jabber:iq:roster' ver='ver11'>
116  <item jid='romeo@example.net'
117        name='Romeo'
118        subscription='both'>
119    <group>Friends</group>
120  </item>
121  <item jid='mercutio@example.com'
122        name='Mercutio'
123        subscription='from'/>
124  <item jid='benvolio@example.net'
125        name='Benvolio'
126        subscription='both'/>
127</query>
128"#.parse().unwrap();
129        let roster = Roster::try_from(elem).unwrap();
130        assert_eq!(roster.ver, Some(String::from("ver11")));
131        assert_eq!(roster.items.len(), 3);
132        assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap());
133        assert_eq!(roster.items[0].name, Some(String::from("Romeo")));
134        assert_eq!(roster.items[0].subscription, Subscription::Both);
135        assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()));
136    }
137
138    #[test]
139    fn test_multiple_groups() {
140        let elem: Element = r#"
141<query xmlns='jabber:iq:roster'>
142  <item jid='test@example.org'>
143    <group>A</group>
144    <group>B</group>
145  </item>
146</query>
147"#.parse().unwrap();
148        let elem1 = elem.clone();
149        let roster = Roster::try_from(elem).unwrap();
150        assert!(roster.ver.is_none());
151        assert_eq!(roster.items.len(), 1);
152        assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap());
153        assert_eq!(roster.items[0].name, None);
154        assert_eq!(roster.items[0].groups.len(), 2);
155        assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap());
156        assert_eq!(roster.items[0].groups[1], Group::from_str("B").unwrap());
157        let elem2 = roster.into();
158        assert!(elem1.compare_to(&elem2));
159    }
160
161    #[test]
162    fn test_set() {
163        let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='nurse@example.com'/></query>".parse().unwrap();
164        let roster = Roster::try_from(elem).unwrap();
165        assert!(roster.ver.is_none());
166        assert_eq!(roster.items.len(), 1);
167
168        let elem: Element = r#"
169<query xmlns='jabber:iq:roster'>
170  <item jid='nurse@example.com'
171        name='Nurse'>
172    <group>Servants</group>
173  </item>
174</query>
175"#.parse().unwrap();
176        let roster = Roster::try_from(elem).unwrap();
177        assert!(roster.ver.is_none());
178        assert_eq!(roster.items.len(), 1);
179        assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap());
180        assert_eq!(roster.items[0].name, Some(String::from("Nurse")));
181        assert_eq!(roster.items[0].groups.len(), 1);
182        assert_eq!(roster.items[0].groups[0], Group::from_str("Servants").unwrap());
183
184        let elem: Element = r#"
185<query xmlns='jabber:iq:roster'>
186  <item jid='nurse@example.com'
187        subscription='remove'/>
188</query>
189"#.parse().unwrap();
190        let roster = Roster::try_from(elem).unwrap();
191        assert!(roster.ver.is_none());
192        assert_eq!(roster.items.len(), 1);
193        assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap());
194        assert!(roster.items[0].name.is_none());
195        assert!(roster.items[0].groups.is_empty());
196        assert_eq!(roster.items[0].subscription, Subscription::Remove);
197    }
198
199    #[test]
200    fn test_invalid() {
201        let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>".parse().unwrap();
202        let error = Roster::try_from(elem).unwrap_err();
203        let message = match error {
204            Error::ParseError(string) => string,
205            _ => panic!(),
206        };
207        assert_eq!(message, "Unknown child in query element.");
208
209        let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>".parse().unwrap();
210        let error = Roster::try_from(elem).unwrap_err();
211        let message = match error {
212            Error::ParseError(string) => string,
213            _ => panic!(),
214        };
215        assert_eq!(message, "Unknown attribute in query element.");
216    }
217
218    #[test]
219    fn test_invalid_item() {
220        let elem: Element = "<query xmlns='jabber:iq:roster'><item/></query>".parse().unwrap();
221        let error = Roster::try_from(elem).unwrap_err();
222        let message = match error {
223            Error::ParseError(string) => string,
224            _ => panic!(),
225        };
226        assert_eq!(message, "Required attribute 'jid' missing.");
227
228        /*
229        let elem: Element = "<query xmlns='jabber:iq:roster'><item jid=''/></query>".parse().unwrap();
230        let error = Roster::try_from(elem).unwrap_err();
231        let error = match error {
232            Error::JidParseError(error) => error,
233            _ => panic!(),
234        };
235        assert_eq!(error.description(), "Invalid JID, I guess?");
236        */
237
238        let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>".parse().unwrap();
239        let error = Roster::try_from(elem).unwrap_err();
240        let message = match error {
241            Error::ParseError(string) => string,
242            _ => panic!(),
243        };
244        assert_eq!(message, "Unknown child in item element.");
245    }
246}