xmpp-rs-mirror/parsers/src/roster.rs

323 lines
10 KiB
Rust
Raw Normal View History

2017-05-28 15:30:43 +00:00
// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// 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/.
2018-12-18 14:32:05 +00:00
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use jid::BareJid;
2017-05-28 15:30:43 +00:00
generate_elem_id!(
/// Represents a group a contact is part of.
2018-12-18 14:32:05 +00:00
Group,
"group",
ROSTER
);
generate_attribute!(
/// The state of your mutual subscription with a contact.
Subscription, "subscription", {
/// The user doesnt have any subscription to this contacts presence,
/// and neither does this contact.
None => "none",
/// Only this contact has a subscription with you, not the opposite.
From => "from",
/// Only you have a subscription with this contact, not the opposite.
To => "to",
/// Both you and your contact are subscribed to each others presence.
Both => "both",
2017-05-28 15:30:43 +00:00
/// In a roster set, this asks the server to remove this contact item
/// from your roster.
Remove => "remove",
}, Default = None
);
2017-05-28 15:30:43 +00:00
generate_attribute!(
/// The sub-state of subscription with a contact.
Ask, "ask", (
/// Pending sub-state of the 'none' subscription state.
Subscribe => "subscribe"
)
);
generate_element!(
2017-11-24 05:09:25 +00:00
/// Contact from the users contact list.
2018-05-14 14:30:28 +00:00
Item, "item", ROSTER,
2017-11-24 05:09:25 +00:00
attributes: [
/// JID of this contact.
jid: Required<BareJid> = "jid",
2017-05-28 15:30:43 +00:00
2017-11-24 05:09:25 +00:00
/// Name of this contact.
name: OptionEmpty<String> = "name",
2017-05-28 15:30:43 +00:00
2017-11-24 05:09:25 +00:00
/// Subscription status of this contact.
subscription: Default<Subscription> = "subscription",
/// Indicates “Pending Out” sub-states for this contact.
ask: Default<Ask> = "ask",
2017-11-24 05:09:25 +00:00
],
2017-05-28 15:30:43 +00:00
2017-11-24 05:09:25 +00:00
children: [
/// Groups this contact is part of.
2018-05-14 14:30:28 +00:00
groups: Vec<Group> = ("group", ROSTER) => Group
2017-11-24 05:09:25 +00:00
]
);
2017-05-28 15:30:43 +00:00
generate_element!(
/// The contact list of the user.
2018-05-14 14:30:28 +00:00
Roster, "query", ROSTER,
attributes: [
/// Version of the contact list.
///
/// This is an opaque string that should only be sent back to the server on
/// a new connection, if this client is storing the contact list between
/// connections.
ver: Option<String> = "ver"
],
children: [
/// List of the contacts of the user.
2018-05-14 14:30:28 +00:00
items: Vec<Item> = ("item", ROSTER) => Item
]
);
2017-05-28 15:30:43 +00:00
impl IqGetPayload for Roster {}
impl IqSetPayload for Roster {}
impl IqResultPayload for Roster {}
2017-05-28 15:30:43 +00:00
#[cfg(test)]
mod tests {
use super::*;
use crate::Element;
use std::str::FromStr;
use xso::error::{Error, FromElementError};
2017-05-28 15:30:43 +00:00
2018-10-28 12:10:48 +00:00
#[cfg(target_pointer_width = "32")]
#[test]
fn test_size() {
assert_size!(Group, 12);
assert_size!(Subscription, 1);
assert_size!(Ask, 1);
2023-06-20 12:14:15 +00:00
assert_size!(Item, 44);
2018-10-28 12:10:48 +00:00
assert_size!(Roster, 24);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(Group, 24);
assert_size!(Subscription, 1);
assert_size!(Ask, 1);
2023-06-20 12:08:58 +00:00
assert_size!(Item, 88);
assert_size!(Roster, 48);
}
2017-05-28 15:30:43 +00:00
#[test]
fn test_get() {
let elem: Element = "<query xmlns='jabber:iq:roster'/>".parse().unwrap();
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert!(roster.items.is_empty());
}
#[test]
fn test_result() {
let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver7'><item jid='nurse@example.com'/><item jid='romeo@example.net'/></query>".parse().unwrap();
let roster = Roster::try_from(elem).unwrap();
assert_eq!(roster.ver, Some(String::from("ver7")));
assert_eq!(roster.items.len(), 2);
let elem2: Element = "<query xmlns='jabber:iq:roster' ver='ver7'><item jid='nurse@example.com'/><item jid='romeo@example.net' name=''/></query>".parse().unwrap();
let roster2 = Roster::try_from(elem2).unwrap();
assert_eq!(roster.items, roster2.items);
2018-12-18 14:32:05 +00:00
let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver9'/>"
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let roster = Roster::try_from(elem).unwrap();
assert_eq!(roster.ver, Some(String::from("ver9")));
assert!(roster.items.is_empty());
let elem: Element = r#"<query xmlns='jabber:iq:roster' ver='ver11'>
2017-05-28 15:30:43 +00:00
<item jid='romeo@example.net'
name='Romeo'
subscription='both'>
<group>Friends</group>
</item>
<item jid='mercutio@example.com'
name='Mercutio'
subscription='from'/>
<item jid='benvolio@example.net'
name='Benvolio'
subscription='both'/>
<item jid='contact@example.org'
subscription='none'
ask='subscribe'
name='MyContact'>
<group>MyBuddies</group>
</item>
2017-05-28 15:30:43 +00:00
</query>
2018-12-18 14:32:05 +00:00
"#
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let roster = Roster::try_from(elem).unwrap();
assert_eq!(roster.ver, Some(String::from("ver11")));
assert_eq!(roster.items.len(), 4);
assert_eq!(
roster.items[0].jid,
BareJid::new("romeo@example.net").unwrap()
);
2017-05-28 15:30:43 +00:00
assert_eq!(roster.items[0].name, Some(String::from("Romeo")));
assert_eq!(roster.items[0].subscription, Subscription::Both);
assert_eq!(roster.items[0].ask, Ask::None);
2018-12-18 14:32:05 +00:00
assert_eq!(
roster.items[0].groups,
vec!(Group::from_str("Friends").unwrap())
);
assert_eq!(
roster.items[3].jid,
BareJid::new("contact@example.org").unwrap()
);
assert_eq!(roster.items[3].name, Some(String::from("MyContact")));
assert_eq!(roster.items[3].subscription, Subscription::None);
assert_eq!(roster.items[3].ask, Ask::Subscribe);
assert_eq!(
roster.items[3].groups,
vec!(Group::from_str("MyBuddies").unwrap())
);
2017-05-28 15:30:43 +00:00
}
#[test]
fn test_multiple_groups() {
let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='test@example.org'><group>A</group><group>B</group></item></query>"
2018-12-18 14:32:05 +00:00
.parse()
.unwrap();
let elem1 = elem.clone();
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert_eq!(roster.items.len(), 1);
assert_eq!(
roster.items[0].jid,
BareJid::new("test@example.org").unwrap()
);
assert_eq!(roster.items[0].name, None);
assert_eq!(roster.items[0].groups.len(), 2);
2017-07-29 04:36:59 +00:00
assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap());
assert_eq!(roster.items[0].groups[1], Group::from_str("B").unwrap());
let elem2 = roster.into();
assert_eq!(elem1, elem2);
}
2017-05-28 15:30:43 +00:00
#[test]
fn test_set() {
2018-12-18 14:32:05 +00:00
let elem: Element =
"<query xmlns='jabber:iq:roster'><item jid='nurse@example.com'/></query>"
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert_eq!(roster.items.len(), 1);
let elem: Element = r#"<query xmlns='jabber:iq:roster'>
2017-05-28 15:30:43 +00:00
<item jid='nurse@example.com'
name='Nurse'>
<group>Servants</group>
</item>
</query>"#
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert_eq!(roster.items.len(), 1);
assert_eq!(
roster.items[0].jid,
BareJid::new("nurse@example.com").unwrap()
);
2017-05-28 15:30:43 +00:00
assert_eq!(roster.items[0].name, Some(String::from("Nurse")));
assert_eq!(roster.items[0].groups.len(), 1);
2018-12-18 14:32:05 +00:00
assert_eq!(
roster.items[0].groups[0],
Group::from_str("Servants").unwrap()
);
2017-05-28 15:30:43 +00:00
let elem: Element = r#"<query xmlns='jabber:iq:roster'>
2017-05-28 15:30:43 +00:00
<item jid='nurse@example.com'
subscription='remove'/>
</query>"#
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert_eq!(roster.items.len(), 1);
assert_eq!(
roster.items[0].jid,
BareJid::new("nurse@example.com").unwrap()
);
2017-05-28 15:30:43 +00:00
assert!(roster.items[0].name.is_none());
assert!(roster.items[0].groups.is_empty());
assert_eq!(roster.items[0].subscription, Subscription::Remove);
2017-05-28 15:30:43 +00:00
}
#[cfg(not(feature = "disable-validation"))]
2017-05-28 15:30:43 +00:00
#[test]
fn test_invalid() {
2018-12-18 14:32:05 +00:00
let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>"
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let error = Roster::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-05-28 15:30:43 +00:00
_ => panic!(),
};
assert_eq!(message, "Unknown child in query element.");
2017-05-28 15:30:43 +00:00
2018-12-18 14:32:05 +00:00
let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>"
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let error = Roster::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-05-28 15:30:43 +00:00
_ => panic!(),
};
assert_eq!(message, "Unknown attribute in query element.");
2017-05-28 15:30:43 +00:00
}
#[test]
fn test_invalid_item() {
2018-12-18 14:32:05 +00:00
let elem: Element = "<query xmlns='jabber:iq:roster'><item/></query>"
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let error = Roster::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-05-28 15:30:43 +00:00
_ => panic!(),
};
assert_eq!(message, "Required attribute 'jid' missing.");
/*
let elem: Element = "<query xmlns='jabber:iq:roster'><item jid=''/></query>".parse().unwrap();
let error = Roster::try_from(elem).unwrap_err();
let error = match error {
Error::JidParseError(error) => error,
_ => panic!(),
};
assert_eq!(error.description(), "Invalid JID, I guess?");
*/
2018-12-18 14:32:05 +00:00
let elem: Element =
"<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>"
.parse()
.unwrap();
2017-05-28 15:30:43 +00:00
let error = Roster::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-05-28 15:30:43 +00:00
_ => panic!(),
};
2017-11-24 05:09:25 +00:00
assert_eq!(message, "Unknown child in item element.");
2017-05-28 15:30:43 +00:00
}
}