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