xmpp-rs/parsers/src/muc/user.rs

802 lines
25 KiB
Rust
Raw Normal View History

// Copyright (c) 2017 Maxime “pep” Buquet <pep@bouah.net>
// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2017-06-26 23:07:12 +01:00
//
// 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/.
use xso::{
error::{Error, FromElementError, FromEventsError},
exports::rxml,
minidom_compat, AsXml, FromXml,
};
use crate::message::MessagePayload;
use crate::ns;
use crate::presence::PresencePayload;
use minidom::Element;
use jid::FullJid;
2017-06-26 23:07:12 +01:00
generate_attribute_enum!(
/// Lists all of the possible status codes used in MUC presences.
2018-05-14 16:30:28 +02:00
Status, "status", MUC_USER, "code", {
/// 100: Inform user that any occupant is allowed to see the user's full JID
NonAnonymousRoom => 100,
2017-06-26 23:07:12 +01:00
/// 101: Inform user that his or her affiliation changed while not in the room
AffiliationChange => 101,
2017-06-26 23:07:12 +01:00
/// 102: Inform occupants that room now shows unavailable members
ConfigShowsUnavailableMembers => 102,
2017-06-26 23:07:12 +01:00
/// 103: Inform occupants that room now does not show unavailable members
ConfigHidesUnavailableMembers => 103,
2017-06-26 23:07:12 +01:00
/// 104: Inform occupants that a non-privacy-related room configuration change has occurred
ConfigNonPrivacyRelated => 104,
2017-06-26 23:07:12 +01:00
/// 110: Inform user that presence refers to itself
SelfPresence => 110,
2017-06-26 23:07:12 +01:00
/// 170: Inform occupants that room logging is now enabled
ConfigRoomLoggingEnabled => 170,
2017-06-26 23:07:12 +01:00
/// 171: Inform occupants that room logging is now disabled
ConfigRoomLoggingDisabled => 171,
2017-06-26 23:07:12 +01:00
/// 172: Inform occupants that the room is now non-anonymous
ConfigRoomNonAnonymous => 172,
2017-06-26 23:07:12 +01:00
/// 173: Inform occupants that the room is now semi-anonymous
ConfigRoomSemiAnonymous => 173,
2017-06-26 23:07:12 +01:00
/// 201: Inform user that a new room has been created
RoomHasBeenCreated => 201,
2017-06-26 23:07:12 +01:00
/// 210: Inform user that service has assigned or modified occupant's roomnick
AssignedNick => 210,
2017-06-26 23:07:12 +01:00
/// 301: Inform user that they have been banned from the room
Banned => 301,
2017-06-26 23:07:12 +01:00
/// 303: Inform all occupants of new room nickname
NewNick => 303,
2017-06-26 23:07:12 +01:00
/// 307: Inform user that they have been kicked from the room
Kicked => 307,
2017-06-26 23:07:12 +01:00
/// 321: Inform user that they are being removed from the room
/// because of an affiliation change
RemovalFromRoom => 321,
2017-06-26 23:07:12 +01:00
/// 322: Inform user that they are being removed from the room
/// because the room has been changed to members-only and the
/// user is not a member
ConfigMembersOnly => 322,
2017-06-26 23:07:12 +01:00
/// 332: Inform user that they are being removed from the room
/// because the MUC service is being shut down
ServiceShutdown => 332,
/// 333: Inform user that they are being removed from the room for technical reasons
ServiceErrorKick => 333,
});
2017-06-26 23:07:12 +01:00
/// Optional \<actor/\> element used in \<item/\> elements inside presence stanzas of type
2017-06-26 23:07:12 +01:00
/// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking
/// purposes. -- CHANGELOG 0.17 (2002-10-23)
///
2017-06-26 23:07:12 +01:00
/// Possesses a 'jid' and a 'nick' attribute, so that an action can be attributed either to a real
/// JID or to a roomnick. -- CHANGELOG 1.25 (2012-02-08)
#[derive(Debug, Clone, PartialEq)]
pub enum Actor {
2018-08-08 20:22:37 +02:00
/// The full JID associated with this user.
Jid(FullJid),
2018-08-08 20:22:37 +02:00
/// The nickname of this user.
2017-06-26 23:07:12 +01:00
Nick(String),
}
impl TryFrom<Element> for Actor {
type Error = FromElementError;
2017-06-26 23:07:12 +01:00
fn try_from(elem: Element) -> Result<Actor, FromElementError> {
2018-05-14 16:30:28 +02:00
check_self!(elem, "actor", MUC_USER);
check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]);
check_no_children!(elem, "actor");
let jid: Option<FullJid> = get_attr!(elem, "jid", Option);
let nick = get_attr!(elem, "nick", Option);
2017-06-26 23:07:12 +01:00
match (jid, nick) {
(Some(_), Some(_)) | (None, None) => {
Err(Error::Other("Either 'jid' or 'nick' attribute is required.").into())
}
2017-06-26 23:07:12 +01:00
(Some(jid), _) => Ok(Actor::Jid(jid)),
(_, Some(nick)) => Ok(Actor::Nick(nick)),
}
}
}
impl FromXml for Actor {
type Builder = minidom_compat::FromEventsViaElement<Actor>;
fn from_events(
qname: rxml::QName,
attrs: rxml::AttrMap,
) -> Result<Self::Builder, FromEventsError> {
if qname.0 != crate::ns::MUC_USER || qname.1 != "actor" {
return Err(FromEventsError::Mismatch { name: qname, attrs });
}
Self::Builder::new(qname, attrs)
}
}
impl From<Actor> for Element {
fn from(actor: Actor) -> Element {
let elem = Element::builder("actor", ns::MUC_USER);
2017-06-26 23:07:12 +01:00
(match actor {
Actor::Jid(jid) => elem.attr("jid", jid),
2017-06-26 23:07:12 +01:00
Actor::Nick(nick) => elem.attr("nick", nick),
2018-12-18 15:32:05 +01:00
})
.build()
2017-06-26 23:07:12 +01:00
}
}
impl AsXml for Actor {
type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>;
fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
minidom_compat::AsItemsViaElement::new(self.clone())
}
}
/// Used to continue a one-to-one discussion in a room, with more than one
/// participant.
2024-07-09 17:01:42 +02:00
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::MUC_USER, name = "continue")]
pub struct Continue {
/// The thread to continue in this room.
#[xml(attribute(default))]
pub thread: Option<String>,
}
2018-08-08 20:22:37 +02:00
generate_elem_id!(
/// A reason for inviting, declining, etc. a request.
2018-12-18 15:32:05 +01:00
Reason,
"reason",
MUC_USER
2018-08-08 20:22:37 +02:00
);
generate_attribute!(
/// The affiliation of an entity with a room, which isn’t tied to its
/// presence in it.
Affiliation, "affiliation", {
/// The user who created the room, or who got appointed by its creator
/// to be their equal.
Owner => "owner",
/// A user who has been empowered by an owner to do administrative
/// operations.
Admin => "admin",
/// A user who is whitelisted to speak in moderated rooms, or to join a
/// member-only room.
Member => "member",
/// A user who has been banned from this room.
Outcast => "outcast",
2017-06-26 23:07:12 +01:00
2018-08-08 20:22:37 +02:00
/// A normal participant.
None => "none",
}, Default = None
);
generate_attribute!(
/// The current role of an entity in a room, it can be changed by an owner
/// or an administrator but will be lost once they leave the room.
Role, "role", {
/// This user can kick other participants, as well as grant and revoke
/// them voice.
Moderator => "moderator",
2017-06-26 23:07:12 +01:00
2018-08-08 20:22:37 +02:00
/// A user who can speak in this room.
Participant => "participant",
2018-08-08 20:22:37 +02:00
/// A user who cannot speak in this room, and must request voice before
/// doing so.
Visitor => "visitor",
/// A user who is absent from the room.
None => "none",
}, Default = None
);
2017-06-26 23:07:12 +01:00
/// An item representing a user in a room.
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::MUC_USER, name = "item")]
pub struct Item {
/// The affiliation of this user with the room.
#[xml(attribute)]
pub affiliation: Affiliation,
2018-08-08 20:22:37 +02:00
/// The real JID of this user, if you are allowed to see it.
#[xml(attribute(default))]
pub jid: Option<FullJid>,
2018-08-08 20:22:37 +02:00
/// The current nickname of this user.
#[xml(attribute(default))]
pub nick: Option<String>,
2018-08-08 20:22:37 +02:00
/// The current role of this user.
#[xml(attribute)]
pub role: Role,
2018-08-08 20:22:37 +02:00
/// The actor affected by this item.
#[xml(child(default))]
pub actor: Option<Actor>,
2018-08-08 20:22:37 +02:00
/// Whether this continues a one-to-one discussion.
#[xml(child(default))]
pub continue_: Option<Continue>,
/// A reason for this item.
#[xml(child(default))]
pub reason: Option<Reason>,
}
2018-07-31 21:32:39 +02:00
impl Item {
2018-08-08 20:22:37 +02:00
/// Creates a new item with the given affiliation and role.
2018-07-31 21:32:39 +02:00
pub fn new(affiliation: Affiliation, role: Role) -> Item {
Item {
affiliation,
role,
jid: None,
nick: None,
actor: None,
continue_: None,
reason: None,
}
}
/// Set a jid for this Item
pub fn with_jid(mut self, jid: FullJid) -> Item {
self.jid = Some(jid);
self
}
/// Set a nick for this Item
pub fn with_nick<S: Into<String>>(mut self, nick: S) -> Item {
self.nick = Some(nick.into());
self
}
/// Set an actor for this Item
pub fn with_actor(mut self, actor: Actor) -> Item {
self.actor = Some(actor);
self
}
/// Set a continue value for this Item
pub fn with_continue<S: Into<String>>(mut self, continue_: S) -> Item {
self.continue_ = Some(Continue {
thread: Some(continue_.into()),
});
self
}
/// Set a reason for this Item
pub fn with_reason<S: Into<String>>(mut self, reason: S) -> Item {
self.reason = Some(Reason(reason.into()));
self
}
2018-07-31 21:32:39 +02:00
}
/// The main muc#user element.
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::MUC_USER, name = "x")]
pub struct MucUser {
/// List of statuses applying to this item.
#[xml(child(n = ..))]
pub status: Vec<Status>,
/// List of items.
#[xml(child(n = ..))]
pub items: Vec<Item>,
}
2017-06-26 23:07:12 +01:00
impl Default for MucUser {
fn default() -> Self {
Self::new()
}
}
impl MucUser {
/// Creates an empty MucUser
pub fn new() -> MucUser {
MucUser {
status: vec![],
items: vec![],
}
}
/// Set statuses for this MucUser
pub fn with_statuses(mut self, status: Vec<Status>) -> MucUser {
self.status = status;
self
}
/// Set items for this MucUser
pub fn with_items(mut self, items: Vec<Item>) -> MucUser {
self.items = items;
self
}
}
impl MessagePayload for MucUser {}
impl PresencePayload for MucUser {}
2017-06-26 23:07:12 +01:00
#[cfg(test)]
mod tests {
use super::*;
use crate::message::Message;
use crate::presence::{Presence, Type as PresenceType};
use jid::Jid;
2017-06-26 23:07:12 +01:00
#[test]
fn test_simple() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
MucUser::try_from(elem).unwrap();
}
#[test]
fn statuses_and_items() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'>
<status code='101'/>
<status code='102'/>
<item affiliation='member' role='moderator'/>
</x>"
.parse()
.unwrap();
let muc_user = MucUser::try_from(elem).unwrap();
assert_eq!(muc_user.status.len(), 2);
assert_eq!(muc_user.status[0], Status::AffiliationChange);
assert_eq!(muc_user.status[1], Status::ConfigShowsUnavailableMembers);
assert_eq!(muc_user.items.len(), 1);
assert_eq!(muc_user.items[0].affiliation, Affiliation::Member);
assert_eq!(muc_user.items[0].role, Role::Moderator);
}
2017-06-26 23:07:12 +01:00
#[test]
fn test_invalid_child() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'>
2017-06-26 23:07:12 +01:00
<coucou/>
</x>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = MucUser::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Unknown child in MucUser element.");
2017-06-26 23:07:12 +01:00
}
#[test]
fn test_serialise() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>"
.parse()
.unwrap();
2018-12-18 15:32:05 +01:00
let muc = MucUser {
status: vec![],
items: vec![],
};
2017-06-26 23:07:12 +01:00
let elem2 = muc.into();
assert_eq!(elem, elem2);
2017-06-26 23:07:12 +01:00
}
#[cfg(not(feature = "disable-validation"))]
2017-06-26 23:07:12 +01:00
#[test]
fn test_invalid_attribute() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = MucUser::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Unknown attribute in MucUser element.");
2017-06-26 23:07:12 +01:00
}
#[test]
fn test_status_simple() {
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
Status::try_from(elem).unwrap();
}
#[test]
fn test_status_invalid() {
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Status::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Required attribute 'code' missing.");
}
#[cfg(not(feature = "disable-validation"))]
2017-06-26 23:07:12 +01:00
#[test]
fn test_status_invalid_child() {
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'>
2017-06-26 23:07:12 +01:00
<foo/>
</status>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Status::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Unknown child in status element.");
}
#[test]
fn test_status_simple_code() {
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='307'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let status = Status::try_from(elem).unwrap();
assert_eq!(status, Status::Kicked);
}
#[test]
fn test_status_invalid_code() {
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='666'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Status::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Invalid status code value.");
2017-06-26 23:07:12 +01:00
}
#[test]
fn test_status_invalid_code2() {
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Status::try_from(elem).unwrap_err();
let error = match error {
FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(error.to_string(), "invalid digit found in string");
2017-06-26 23:07:12 +01:00
}
#[test]
fn test_actor_required_attributes() {
let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Actor::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Either 'jid' or 'nick' attribute is required.");
}
#[test]
fn test_actor_required_attributes2() {
let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user'
2017-06-26 23:07:12 +01:00
jid='foo@bar/baz'
nick='baz'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Actor::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Either 'jid' or 'nick' attribute is required.");
}
#[test]
fn test_actor_jid() {
let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user'
jid='foo@bar/baz'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let actor = Actor::try_from(elem).unwrap();
let jid = match actor {
Actor::Jid(jid) => jid,
_ => panic!(),
};
assert_eq!(jid, "foo@bar/baz".parse::<FullJid>().unwrap());
2017-06-26 23:07:12 +01:00
}
#[test]
fn test_actor_nick() {
let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user' nick='baz'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let actor = Actor::try_from(elem).unwrap();
let nick = match actor {
Actor::Nick(nick) => nick,
_ => panic!(),
};
assert_eq!(nick, "baz".to_owned());
}
#[test]
fn test_continue_simple() {
let elem: Element = "<continue xmlns='http://jabber.org/protocol/muc#user'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
Continue::try_from(elem).unwrap();
}
#[test]
fn test_continue_thread_attribute() {
let elem: Element = "<continue xmlns='http://jabber.org/protocol/muc#user'
thread='foo'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let continue_ = Continue::try_from(elem).unwrap();
assert_eq!(continue_.thread, Some("foo".to_owned()));
2017-06-26 23:07:12 +01:00
}
#[test]
fn test_continue_invalid() {
let elem: Element =
"<continue xmlns='http://jabber.org/protocol/muc#user'><foobar/></continue>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let continue_ = Continue::try_from(elem).unwrap_err();
let message = match continue_ {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Unknown child in Continue element.".to_owned());
2017-06-26 23:07:12 +01:00
}
#[test]
fn test_reason_simple() {
let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user'>Reason</reason>"
2018-12-18 15:32:05 +01:00
.parse()
.unwrap();
let elem2 = elem.clone();
2017-06-26 23:07:12 +01:00
let reason = Reason::try_from(elem).unwrap();
assert_eq!(reason.0, "Reason".to_owned());
let elem3 = reason.into();
assert_eq!(elem2, elem3);
2017-06-26 23:07:12 +01:00
}
#[cfg(not(feature = "disable-validation"))]
2017-06-26 23:07:12 +01:00
#[test]
fn test_reason_invalid_attribute() {
let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Reason::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Unknown attribute in Reason element.".to_owned());
2017-06-26 23:07:12 +01:00
}
#[cfg(not(feature = "disable-validation"))]
2017-06-26 23:07:12 +01:00
#[test]
fn test_reason_invalid() {
let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user'>
2017-06-26 23:07:12 +01:00
<foobar/>
</reason>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Reason::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Unknown child in Reason element.".to_owned());
2017-06-26 23:07:12 +01:00
}
#[cfg(not(feature = "disable-validation"))]
2017-06-26 23:07:12 +01:00
#[test]
2018-12-18 15:32:05 +01:00
fn test_item_invalid_attr() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
affiliation='member'
role='moderator'
foo='bar'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Item::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(message, "Unknown attribute in Item element.".to_owned());
2017-06-26 23:07:12 +01:00
}
#[test]
2018-12-18 15:32:05 +01:00
fn test_item_affiliation_role_attr() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
2017-06-26 23:07:12 +01:00
affiliation='member'
role='moderator'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
Item::try_from(elem).unwrap();
}
#[test]
2018-12-18 15:32:05 +01:00
fn test_item_affiliation_role_invalid_attr() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
affiliation='member'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Item::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
assert_eq!(
message,
"Required attribute field 'role' on Item element missing.".to_owned()
);
2017-06-26 23:07:12 +01:00
}
#[test]
2018-12-18 15:32:05 +01:00
fn test_item_nick_attr() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
2017-06-26 23:07:12 +01:00
affiliation='member'
role='moderator'
nick='foobar'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let item = Item::try_from(elem).unwrap();
match item {
Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())),
}
}
#[test]
2018-12-18 15:32:05 +01:00
fn test_item_affiliation_role_invalid_attr2() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
role='moderator'/>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let error = Item::try_from(elem).unwrap_err();
let message = match error {
FromElementError::Invalid(Error::Other(string)) => string,
2017-06-26 23:07:12 +01:00
_ => panic!(),
};
2018-12-18 15:32:05 +01:00
assert_eq!(
message,
"Required attribute field 'affiliation' on Item element missing.".to_owned()
2018-12-18 15:32:05 +01:00
);
2017-06-26 23:07:12 +01:00
}
#[test]
2018-12-18 15:32:05 +01:00
fn test_item_role_actor_child() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
2017-06-26 23:07:12 +01:00
affiliation='member'
role='moderator'>
<actor nick='foobar'/>
</item>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let item = Item::try_from(elem).unwrap();
match item {
2018-12-18 15:32:05 +01:00
Item { actor, .. } => assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
2017-06-26 23:07:12 +01:00
}
}
#[test]
2018-12-18 15:32:05 +01:00
fn test_item_role_continue_child() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
2017-06-26 23:07:12 +01:00
affiliation='member'
role='moderator'>
<continue thread='foobar'/>
</item>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let item = Item::try_from(elem).unwrap();
2018-12-18 15:32:05 +01:00
let continue_1 = Continue {
thread: Some("foobar".to_owned()),
};
2017-06-26 23:07:12 +01:00
match item {
2018-12-18 15:32:05 +01:00
Item {
continue_: Some(continue_2),
..
} => assert_eq!(continue_2.thread, continue_1.thread),
2017-06-26 23:07:12 +01:00
_ => panic!(),
}
}
#[test]
2018-12-18 15:32:05 +01:00
fn test_item_role_reason_child() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
2017-06-26 23:07:12 +01:00
affiliation='member'
role='moderator'>
<reason>foobar</reason>
</item>"
.parse()
.unwrap();
2017-06-26 23:07:12 +01:00
let item = Item::try_from(elem).unwrap();
match item {
2018-12-18 15:32:05 +01:00
Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))),
2017-06-26 23:07:12 +01:00
}
}
#[test]
fn test_serialize_item() {
let reference: Element = "<item xmlns='http://jabber.org/protocol/muc#user' affiliation='member' role='moderator'><actor nick='foobar'/><continue thread='foobar'/><reason>foobar</reason></item>"
.parse()
.unwrap();
let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user' nick='foobar'/>"
2019-12-30 07:35:32 +01:40
.parse()
.unwrap();
let actor = Actor::try_from(elem).unwrap();
2019-12-30 07:35:32 +01:40
let elem: Element =
"<continue xmlns='http://jabber.org/protocol/muc#user' thread='foobar'/>"
.parse()
.unwrap();
let continue_ = Continue::try_from(elem).unwrap();
let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user'>foobar</reason>"
2019-12-30 07:35:32 +01:40
.parse()
.unwrap();
let reason = Reason::try_from(elem).unwrap();
let item = Item {
affiliation: Affiliation::Member,
role: Role::Moderator,
jid: None,
nick: None,
actor: Some(actor),
reason: Some(reason),
continue_: Some(continue_),
};
let serialized: Element = item.into();
assert_eq!(serialized, reference);
}
#[test]
fn presence_payload() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>"
.parse()
.unwrap();
let presence = Presence::new(PresenceType::None).with_payloads(vec![elem]);
assert_eq!(presence.payloads.len(), 1);
}
#[test]
fn message_payload() {
let jid: Jid = Jid::new("louise@example.com").unwrap();
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>"
.parse()
.unwrap();
let message = Message::new(jid).with_payloads(vec![elem]);
assert_eq!(message.payloads.len(), 1);
}
2017-06-26 23:07:12 +01:00
}