muc::Muc and muc::MucUser
This commit is contained in:
parent
8ac4602240
commit
27cf9a6298
4 changed files with 949 additions and 346 deletions
346
src/muc.rs
346
src/muc.rs
|
|
@ -1,346 +0,0 @@
|
||||||
// Copyright (c) 2017 Maxime “pep” Buquet <pep+code@bouah.net>
|
|
||||||
//
|
|
||||||
// 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 std::convert::TryFrom;
|
|
||||||
|
|
||||||
use minidom::{Element, IntoElements, ElementEmitter};
|
|
||||||
|
|
||||||
use error::Error;
|
|
||||||
|
|
||||||
use ns;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Muc;
|
|
||||||
|
|
||||||
impl TryFrom<Element> for Muc {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(elem: Element) -> Result<Muc, Error> {
|
|
||||||
if !elem.is("x", ns::MUC) {
|
|
||||||
return Err(Error::ParseError("This is not an x element."));
|
|
||||||
}
|
|
||||||
for _ in elem.children() {
|
|
||||||
return Err(Error::ParseError("Unknown child in x element."));
|
|
||||||
}
|
|
||||||
for _ in elem.attrs() {
|
|
||||||
return Err(Error::ParseError("Unknown attribute in x element."));
|
|
||||||
}
|
|
||||||
Ok(Muc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Element> for Muc {
|
|
||||||
fn into(self) -> Element {
|
|
||||||
Element::builder("x")
|
|
||||||
.ns(ns::MUC)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Status {
|
|
||||||
// 100
|
|
||||||
NonAnonymousRoom,
|
|
||||||
|
|
||||||
// 101
|
|
||||||
AffiliationChange,
|
|
||||||
|
|
||||||
// 102
|
|
||||||
ConfigShowsUnavailableMembers,
|
|
||||||
|
|
||||||
// 103
|
|
||||||
ConfigHidesUnavailableMembers,
|
|
||||||
|
|
||||||
// 104
|
|
||||||
ConfigNonPrivacyRelated,
|
|
||||||
|
|
||||||
// 110
|
|
||||||
SelfPresence,
|
|
||||||
|
|
||||||
// 170
|
|
||||||
ConfigRoomLoggingEnabled,
|
|
||||||
|
|
||||||
// 171
|
|
||||||
ConfigRoomLoggingDisabled,
|
|
||||||
|
|
||||||
// 172
|
|
||||||
ConfigRoomNonAnonymous,
|
|
||||||
|
|
||||||
// 173
|
|
||||||
ConfigRoomSemiAnonymous,
|
|
||||||
|
|
||||||
// 201
|
|
||||||
RoomHasBeenCreated,
|
|
||||||
|
|
||||||
// 210
|
|
||||||
AssignedNick,
|
|
||||||
|
|
||||||
// 301
|
|
||||||
Banned,
|
|
||||||
|
|
||||||
// 303
|
|
||||||
NewNick,
|
|
||||||
|
|
||||||
// 307
|
|
||||||
Kicked,
|
|
||||||
|
|
||||||
// 321
|
|
||||||
RemovalFromRoom,
|
|
||||||
|
|
||||||
// 322
|
|
||||||
ConfigMembersOnly,
|
|
||||||
|
|
||||||
// 332
|
|
||||||
ServiceShutdown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Element> for Status {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(elem: Element) -> Result<Status, Error> {
|
|
||||||
if !elem.is("status", ns::MUC_USER) {
|
|
||||||
return Err(Error::ParseError("This is not a status element."));
|
|
||||||
}
|
|
||||||
for _ in elem.children() {
|
|
||||||
return Err(Error::ParseError("Unknown child in status element."));
|
|
||||||
}
|
|
||||||
for (attr, _) in elem.attrs() {
|
|
||||||
if attr != "code" {
|
|
||||||
return Err(Error::ParseError("Unknown attribute in status element."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let code = get_attr!(elem, "code", required);
|
|
||||||
|
|
||||||
Ok(match code {
|
|
||||||
100 => Status::NonAnonymousRoom,
|
|
||||||
101 => Status::AffiliationChange,
|
|
||||||
102 => Status::ConfigShowsUnavailableMembers,
|
|
||||||
103 => Status::ConfigHidesUnavailableMembers,
|
|
||||||
104 => Status::ConfigNonPrivacyRelated,
|
|
||||||
110 => Status::SelfPresence,
|
|
||||||
170 => Status::ConfigRoomLoggingEnabled,
|
|
||||||
171 => Status::ConfigRoomLoggingDisabled,
|
|
||||||
172 => Status::ConfigRoomNonAnonymous,
|
|
||||||
173 => Status::ConfigRoomSemiAnonymous,
|
|
||||||
201 => Status::RoomHasBeenCreated,
|
|
||||||
210 => Status::AssignedNick,
|
|
||||||
301 => Status::Banned,
|
|
||||||
303 => Status::NewNick,
|
|
||||||
307 => Status::Kicked,
|
|
||||||
321 => Status::RemovalFromRoom,
|
|
||||||
322 => Status::ConfigMembersOnly,
|
|
||||||
332 => Status::ServiceShutdown,
|
|
||||||
_ => return Err(Error::ParseError("Invalid status code.")),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Element> for Status {
|
|
||||||
fn into(self) -> Element {
|
|
||||||
Element::builder("status")
|
|
||||||
.ns(ns::MUC_USER)
|
|
||||||
.attr("code", match self {
|
|
||||||
Status::NonAnonymousRoom => 100,
|
|
||||||
Status::AffiliationChange => 101,
|
|
||||||
Status::ConfigShowsUnavailableMembers => 102,
|
|
||||||
Status::ConfigHidesUnavailableMembers => 103,
|
|
||||||
Status::ConfigNonPrivacyRelated => 104,
|
|
||||||
Status::SelfPresence => 110,
|
|
||||||
Status::ConfigRoomLoggingEnabled => 170,
|
|
||||||
Status::ConfigRoomLoggingDisabled => 171,
|
|
||||||
Status::ConfigRoomNonAnonymous => 172,
|
|
||||||
Status::ConfigRoomSemiAnonymous => 173,
|
|
||||||
Status::RoomHasBeenCreated => 201,
|
|
||||||
Status::AssignedNick => 210,
|
|
||||||
Status::Banned => 301,
|
|
||||||
Status::NewNick => 303,
|
|
||||||
Status::Kicked => 307,
|
|
||||||
Status::RemovalFromRoom => 321,
|
|
||||||
Status::ConfigMembersOnly => 322,
|
|
||||||
Status::ServiceShutdown => 332,
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoElements for Status {
|
|
||||||
fn into_elements(self, emitter: &mut ElementEmitter) {
|
|
||||||
emitter.append_child(self.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MucUser {
|
|
||||||
status: Vec<Status>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Element> for MucUser {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(elem: Element) -> Result<MucUser, Error> {
|
|
||||||
if !elem.is("x", ns::MUC_USER) {
|
|
||||||
return Err(Error::ParseError("This is not an x element."));
|
|
||||||
}
|
|
||||||
let mut status = vec!();
|
|
||||||
for child in elem.children() {
|
|
||||||
if child.is("status", ns::MUC_USER) {
|
|
||||||
status.push(Status::try_from(child.clone())?);
|
|
||||||
} else {
|
|
||||||
return Err(Error::ParseError("Unknown child in x element."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _ in elem.attrs() {
|
|
||||||
return Err(Error::ParseError("Unknown attribute in x element."));
|
|
||||||
}
|
|
||||||
Ok(MucUser {
|
|
||||||
status: status,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Element> for MucUser {
|
|
||||||
fn into(self) -> Element {
|
|
||||||
Element::builder("x")
|
|
||||||
.ns(ns::MUC_USER)
|
|
||||||
.append(self.status)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::error::Error as StdError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_muc_simple() {
|
|
||||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
|
|
||||||
Muc::try_from(elem).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_muc_invalid_child() {
|
|
||||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>".parse().unwrap();
|
|
||||||
let error = Muc::try_from(elem).unwrap_err();
|
|
||||||
let message = match error {
|
|
||||||
Error::ParseError(string) => string,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
assert_eq!(message, "Unknown child in x element.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_muc_serialise() {
|
|
||||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
|
|
||||||
let muc = Muc;
|
|
||||||
let elem2 = muc.into();
|
|
||||||
assert_eq!(elem, elem2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_muc_invalid_attribute() {
|
|
||||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>".parse().unwrap();
|
|
||||||
let error = Muc::try_from(elem).unwrap_err();
|
|
||||||
let message = match error {
|
|
||||||
Error::ParseError(string) => string,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
assert_eq!(message, "Unknown attribute in x element.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_muc_user_simple() {
|
|
||||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>".parse().unwrap();
|
|
||||||
MucUser::try_from(elem).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_muc_user_invalid_child() {
|
|
||||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'><coucou/></x>".parse().unwrap();
|
|
||||||
let error = MucUser::try_from(elem).unwrap_err();
|
|
||||||
let message = match error {
|
|
||||||
Error::ParseError(string) => string,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
assert_eq!(message, "Unknown child in x element.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_muc_user_serialise() {
|
|
||||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>".parse().unwrap();
|
|
||||||
let muc = MucUser { status: vec!() };
|
|
||||||
let elem2 = muc.into();
|
|
||||||
assert_eq!(elem, elem2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_muc_user_invalid_attribute() {
|
|
||||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>".parse().unwrap();
|
|
||||||
let error = MucUser::try_from(elem).unwrap_err();
|
|
||||||
let message = match error {
|
|
||||||
Error::ParseError(string) => string,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
assert_eq!(message, "Unknown attribute in x element.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_status_simple() {
|
|
||||||
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>".parse().unwrap();
|
|
||||||
Status::try_from(elem).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_status_invalid() {
|
|
||||||
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user'/>".parse().unwrap();
|
|
||||||
let error = Status::try_from(elem).unwrap_err();
|
|
||||||
let message = match error {
|
|
||||||
Error::ParseError(string) => string,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
assert_eq!(message, "Required attribute 'code' missing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_status_invalid_child() {
|
|
||||||
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'><foo/></status>".parse().unwrap();
|
|
||||||
let error = Status::try_from(elem).unwrap_err();
|
|
||||||
let message = match error {
|
|
||||||
Error::ParseError(string) => string,
|
|
||||||
_ => 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();
|
|
||||||
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();
|
|
||||||
let error = Status::try_from(elem).unwrap_err();
|
|
||||||
let message = match error {
|
|
||||||
Error::ParseError(string) => string,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
assert_eq!(message, "Invalid status code.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_status_invalid_code2() {
|
|
||||||
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>".parse().unwrap();
|
|
||||||
let error = Status::try_from(elem).unwrap_err();
|
|
||||||
let error = match error {
|
|
||||||
Error::ParseIntError(error) => error,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
assert_eq!(error.description(), "invalid digit found in string");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
src/muc/mod.rs
Normal file
11
src/muc/mod.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) 2017 Maxime “pep” Buquet <pep+code@bouah.net>
|
||||||
|
//
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
pub mod muc;
|
||||||
|
pub mod user;
|
||||||
|
|
||||||
|
pub use self::muc::Muc;
|
||||||
|
pub use self::user::MucUser;
|
||||||
108
src/muc/muc.rs
Normal file
108
src/muc/muc.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
// Copyright (c) 2017 Maxime “pep” Buquet <pep+code@bouah.net>
|
||||||
|
//
|
||||||
|
// 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 std::convert::TryFrom;
|
||||||
|
|
||||||
|
use minidom::Element;
|
||||||
|
|
||||||
|
use error::Error;
|
||||||
|
|
||||||
|
use ns;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Muc {
|
||||||
|
pub password: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Element> for Muc {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(elem: Element) -> Result<Muc, Error> {
|
||||||
|
if !elem.is("x", ns::MUC) {
|
||||||
|
return Err(Error::ParseError("This is not an x element."));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut password = None;
|
||||||
|
for child in elem.children() {
|
||||||
|
if child.is("password", ns::MUC) {
|
||||||
|
password = Some(child.text());
|
||||||
|
} else {
|
||||||
|
return Err(Error::ParseError("Unknown child in x element."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in elem.attrs() {
|
||||||
|
return Err(Error::ParseError("Unknown attribute in x element."));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Muc {
|
||||||
|
password: password,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Element> for Muc {
|
||||||
|
fn into(self) -> Element {
|
||||||
|
Element::builder("x")
|
||||||
|
.ns(ns::MUC)
|
||||||
|
.append(self.password)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_muc_simple() {
|
||||||
|
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
|
||||||
|
Muc::try_from(elem).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_muc_invalid_child() {
|
||||||
|
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>".parse().unwrap();
|
||||||
|
let error = Muc::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Unknown child in x element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_muc_serialise() {
|
||||||
|
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
|
||||||
|
let muc = Muc {
|
||||||
|
password: None,
|
||||||
|
};
|
||||||
|
let elem2 = muc.into();
|
||||||
|
assert_eq!(elem, elem2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_muc_invalid_attribute() {
|
||||||
|
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>".parse().unwrap();
|
||||||
|
let error = Muc::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Unknown attribute in x element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_muc_simple_password() {
|
||||||
|
let elem: Element = "
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc'>
|
||||||
|
<password>coucou</password>
|
||||||
|
</x>"
|
||||||
|
.parse().unwrap();
|
||||||
|
let muc = Muc::try_from(elem).unwrap();
|
||||||
|
assert_eq!(muc.password, Some("coucou".to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
830
src/muc/user.rs
Normal file
830
src/muc/user.rs
Normal file
|
|
@ -0,0 +1,830 @@
|
||||||
|
// Copyright (c) 2017 Maxime “pep” Buquet <pep+code@bouah.net>
|
||||||
|
//
|
||||||
|
// 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 std::convert::TryFrom;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter};
|
||||||
|
|
||||||
|
use jid::Jid;
|
||||||
|
|
||||||
|
use error::Error;
|
||||||
|
|
||||||
|
use ns;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Status {
|
||||||
|
/// Status: 100
|
||||||
|
NonAnonymousRoom,
|
||||||
|
|
||||||
|
/// Status: 101
|
||||||
|
AffiliationChange,
|
||||||
|
|
||||||
|
/// Status: 102
|
||||||
|
ConfigShowsUnavailableMembers,
|
||||||
|
|
||||||
|
/// Status: 103
|
||||||
|
ConfigHidesUnavailableMembers,
|
||||||
|
|
||||||
|
/// Status: 104
|
||||||
|
ConfigNonPrivacyRelated,
|
||||||
|
|
||||||
|
/// Status: 110
|
||||||
|
SelfPresence,
|
||||||
|
|
||||||
|
/// Status: 170
|
||||||
|
ConfigRoomLoggingEnabled,
|
||||||
|
|
||||||
|
/// Status: 171
|
||||||
|
ConfigRoomLoggingDisabled,
|
||||||
|
|
||||||
|
/// Status: 172
|
||||||
|
ConfigRoomNonAnonymous,
|
||||||
|
|
||||||
|
/// Status: 173
|
||||||
|
ConfigRoomSemiAnonymous,
|
||||||
|
|
||||||
|
/// Status: 201
|
||||||
|
RoomHasBeenCreated,
|
||||||
|
|
||||||
|
/// Status: 210
|
||||||
|
AssignedNick,
|
||||||
|
|
||||||
|
/// Status: 301
|
||||||
|
Banned,
|
||||||
|
|
||||||
|
/// Status: 303
|
||||||
|
NewNick,
|
||||||
|
|
||||||
|
/// Status: 307
|
||||||
|
Kicked,
|
||||||
|
|
||||||
|
/// Status: 321
|
||||||
|
RemovalFromRoom,
|
||||||
|
|
||||||
|
/// Status: 322
|
||||||
|
ConfigMembersOnly,
|
||||||
|
|
||||||
|
/// Status: 332
|
||||||
|
ServiceShutdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Element> for Status {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(elem: Element) -> Result<Status, Error> {
|
||||||
|
if !elem.is("status", ns::MUC_USER) {
|
||||||
|
return Err(Error::ParseError("This is not a status element."));
|
||||||
|
}
|
||||||
|
for _ in elem.children() {
|
||||||
|
return Err(Error::ParseError("Unknown child in status element."));
|
||||||
|
}
|
||||||
|
for (attr, _) in elem.attrs() {
|
||||||
|
if attr != "code" {
|
||||||
|
return Err(Error::ParseError("Unknown attribute in status element."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let code = get_attr!(elem, "code", required);
|
||||||
|
|
||||||
|
Ok(match code {
|
||||||
|
100 => Status::NonAnonymousRoom,
|
||||||
|
101 => Status::AffiliationChange,
|
||||||
|
102 => Status::ConfigShowsUnavailableMembers,
|
||||||
|
103 => Status::ConfigHidesUnavailableMembers,
|
||||||
|
104 => Status::ConfigNonPrivacyRelated,
|
||||||
|
110 => Status::SelfPresence,
|
||||||
|
170 => Status::ConfigRoomLoggingEnabled,
|
||||||
|
171 => Status::ConfigRoomLoggingDisabled,
|
||||||
|
172 => Status::ConfigRoomNonAnonymous,
|
||||||
|
173 => Status::ConfigRoomSemiAnonymous,
|
||||||
|
201 => Status::RoomHasBeenCreated,
|
||||||
|
210 => Status::AssignedNick,
|
||||||
|
301 => Status::Banned,
|
||||||
|
303 => Status::NewNick,
|
||||||
|
307 => Status::Kicked,
|
||||||
|
321 => Status::RemovalFromRoom,
|
||||||
|
322 => Status::ConfigMembersOnly,
|
||||||
|
332 => Status::ServiceShutdown,
|
||||||
|
_ => return Err(Error::ParseError("Invalid status code.")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Element> for Status {
|
||||||
|
fn into(self) -> Element {
|
||||||
|
Element::builder("status")
|
||||||
|
.ns(ns::MUC_USER)
|
||||||
|
.attr("code", match self {
|
||||||
|
Status::NonAnonymousRoom => 100,
|
||||||
|
Status::AffiliationChange => 101,
|
||||||
|
Status::ConfigShowsUnavailableMembers => 102,
|
||||||
|
Status::ConfigHidesUnavailableMembers => 103,
|
||||||
|
Status::ConfigNonPrivacyRelated => 104,
|
||||||
|
Status::SelfPresence => 110,
|
||||||
|
Status::ConfigRoomLoggingEnabled => 170,
|
||||||
|
Status::ConfigRoomLoggingDisabled => 171,
|
||||||
|
Status::ConfigRoomNonAnonymous => 172,
|
||||||
|
Status::ConfigRoomSemiAnonymous => 173,
|
||||||
|
Status::RoomHasBeenCreated => 201,
|
||||||
|
Status::AssignedNick => 210,
|
||||||
|
Status::Banned => 301,
|
||||||
|
Status::NewNick => 303,
|
||||||
|
Status::Kicked => 307,
|
||||||
|
Status::RemovalFromRoom => 321,
|
||||||
|
Status::ConfigMembersOnly => 322,
|
||||||
|
Status::ServiceShutdown => 332,
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElements for Status {
|
||||||
|
fn into_elements(self, emitter: &mut ElementEmitter) {
|
||||||
|
emitter.append_child(self.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Optional <actor/> element used in <item/> elements inside presence stanzas of type
|
||||||
|
/// "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)
|
||||||
|
/// 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 {
|
||||||
|
Jid(Jid),
|
||||||
|
Nick(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Element> for Actor {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(elem: Element) -> Result<Actor, Error> {
|
||||||
|
if !elem.is("actor", ns::MUC_USER) {
|
||||||
|
return Err(Error::ParseError("This is not a actor element."));
|
||||||
|
}
|
||||||
|
for _ in elem.children() {
|
||||||
|
return Err(Error::ParseError("Unknown child in actor element."));
|
||||||
|
}
|
||||||
|
for (attr, _) in elem.attrs() {
|
||||||
|
if attr != "jid" && attr != "nick" {
|
||||||
|
return Err(Error::ParseError("Unknown attribute in actor element."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let jid: Option<Jid> = get_attr!(elem, "jid", optional);
|
||||||
|
let nick = get_attr!(elem, "nick", optional);
|
||||||
|
|
||||||
|
match (jid, nick) {
|
||||||
|
(Some(_), Some(_))
|
||||||
|
| (None, None) =>
|
||||||
|
return Err(Error::ParseError("Either 'jid' or 'nick' attribute is required.")),
|
||||||
|
(Some(jid), _) => Ok(Actor::Jid(jid)),
|
||||||
|
(_, Some(nick)) => Ok(Actor::Nick(nick)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Element> for Actor {
|
||||||
|
fn into(self) -> Element {
|
||||||
|
let elem = Element::builder("actor").ns(ns::MUC_USER);
|
||||||
|
|
||||||
|
(match self {
|
||||||
|
Actor::Jid(jid) => elem.attr("jid", String::from(jid)),
|
||||||
|
Actor::Nick(nick) => elem.attr("nick", nick),
|
||||||
|
}).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElements for Actor {
|
||||||
|
fn into_elements(self, emitter: &mut ElementEmitter) {
|
||||||
|
emitter.append_child(self.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Continue {
|
||||||
|
thread: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Element> for Continue {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(elem: Element) -> Result<Continue, Error> {
|
||||||
|
if !elem.is("continue", ns::MUC_USER) {
|
||||||
|
return Err(Error::ParseError("This is not a continue element."));
|
||||||
|
}
|
||||||
|
for _ in elem.children() {
|
||||||
|
return Err(Error::ParseError("Unknown child in continue element."));
|
||||||
|
}
|
||||||
|
for (attr, _) in elem.attrs() {
|
||||||
|
if attr != "thread" {
|
||||||
|
return Err(Error::ParseError("Unknown attribute in continue element."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Continue { thread: get_attr!(elem, "thread", optional) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Element> for Continue {
|
||||||
|
fn into(self) -> Element {
|
||||||
|
Element::builder("continue")
|
||||||
|
.ns(ns::MUC_USER)
|
||||||
|
.attr("thread", self.thread)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElements for Continue {
|
||||||
|
fn into_elements(self, emitter: &mut ElementEmitter) {
|
||||||
|
emitter.append_child(self.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Reason(String);
|
||||||
|
|
||||||
|
impl TryFrom<Element> for Reason {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(elem: Element) -> Result<Reason, Error> {
|
||||||
|
if !elem.is("reason", ns::MUC_USER) {
|
||||||
|
return Err(Error::ParseError("This is not a reason element."));
|
||||||
|
}
|
||||||
|
for _ in elem.children() {
|
||||||
|
return Err(Error::ParseError("Unknown child in reason element."));
|
||||||
|
}
|
||||||
|
for _ in elem.attrs() {
|
||||||
|
return Err(Error::ParseError("Unknown attribute in reason element."));
|
||||||
|
}
|
||||||
|
Ok(Reason(elem.text()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Element> for Reason {
|
||||||
|
fn into(self) -> Element {
|
||||||
|
Element::builder("reason")
|
||||||
|
.ns(ns::MUC_USER)
|
||||||
|
.append(self.0)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElements for Reason {
|
||||||
|
fn into_elements(self, emitter: &mut ElementEmitter) {
|
||||||
|
emitter.append_child(self.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Affiliation {
|
||||||
|
Owner,
|
||||||
|
Admin,
|
||||||
|
Member,
|
||||||
|
Outcast,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Affiliation {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Affiliation, Error> {
|
||||||
|
Ok(match s {
|
||||||
|
"owner" => Affiliation::Owner,
|
||||||
|
"admin" => Affiliation::Admin,
|
||||||
|
"member" => Affiliation::Member,
|
||||||
|
"outcast" => Affiliation::Outcast,
|
||||||
|
"none" => Affiliation::None,
|
||||||
|
|
||||||
|
_ => return Err(Error::ParseError("Unknown affiliation.")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAttributeValue for Affiliation {
|
||||||
|
fn into_attribute_value(self) -> Option<String> {
|
||||||
|
Some(String::from(match self {
|
||||||
|
Affiliation::Owner => "owner",
|
||||||
|
Affiliation::Admin => "admin",
|
||||||
|
Affiliation::Member => "member",
|
||||||
|
Affiliation::Outcast => "outcast",
|
||||||
|
Affiliation::None => "none",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Role {
|
||||||
|
Moderator,
|
||||||
|
Participant,
|
||||||
|
Visitor,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Role {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Role, Error> {
|
||||||
|
Ok(match s {
|
||||||
|
"moderator" => Role::Moderator,
|
||||||
|
"participant" => Role::Participant,
|
||||||
|
"visitor" => Role::Visitor,
|
||||||
|
"none" => Role::None,
|
||||||
|
|
||||||
|
_ => return Err(Error::ParseError("Unknown role.")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAttributeValue for Role {
|
||||||
|
fn into_attribute_value(self) -> Option<String> {
|
||||||
|
Some(String::from(match self {
|
||||||
|
Role::Moderator => "moderator",
|
||||||
|
Role::Participant => "participant",
|
||||||
|
Role::Visitor => "visitor",
|
||||||
|
Role::None => "none",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Item {
|
||||||
|
pub affiliation: Affiliation,
|
||||||
|
pub jid: Option<Jid>,
|
||||||
|
pub nick: Option<String>,
|
||||||
|
pub role: Role,
|
||||||
|
pub actor: Option<Actor>,
|
||||||
|
pub continue_: Option<Continue>,
|
||||||
|
pub reason: Option<Reason>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Element> for Item {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(elem: Element) -> Result<Item, Error> {
|
||||||
|
if !elem.is("item", ns::MUC_USER) {
|
||||||
|
return Err(Error::ParseError("This is not a item element."));
|
||||||
|
}
|
||||||
|
let mut actor: Option<Actor> = None;
|
||||||
|
let mut continue_: Option<Continue> = None;
|
||||||
|
let mut reason: Option<Reason> = None;
|
||||||
|
for child in elem.children() {
|
||||||
|
if child.is("actor", ns::MUC_USER) {
|
||||||
|
actor = Some(child.clone().try_into()?);
|
||||||
|
} else if child.is("continue", ns::MUC_USER) {
|
||||||
|
continue_ = Some(child.clone().try_into()?);
|
||||||
|
} else if child.is("reason", ns::MUC_USER) {
|
||||||
|
reason = Some(child.clone().try_into()?);
|
||||||
|
} else {
|
||||||
|
return Err(Error::ParseError("Unknown child in item element."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (attr, _) in elem.attrs() {
|
||||||
|
if attr != "affiliation" && attr != "jid" &&
|
||||||
|
attr != "nick" && attr != "role" {
|
||||||
|
return Err(Error::ParseError("Unknown attribute in item element."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let affiliation: Affiliation = get_attr!(elem, "affiliation", required);
|
||||||
|
let jid: Option<Jid> = get_attr!(elem, "jid", optional);
|
||||||
|
let nick: Option<String> = get_attr!(elem, "nick", optional);
|
||||||
|
let role: Role = get_attr!(elem, "role", required);
|
||||||
|
|
||||||
|
Ok(Item{
|
||||||
|
affiliation: affiliation,
|
||||||
|
jid: jid,
|
||||||
|
nick: nick,
|
||||||
|
role: role,
|
||||||
|
actor: actor,
|
||||||
|
continue_: continue_,
|
||||||
|
reason: reason,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Element> for Item {
|
||||||
|
fn into(self) -> Element {
|
||||||
|
Element::builder("item")
|
||||||
|
.ns(ns::MUC_USER)
|
||||||
|
.attr("affiliation", self.affiliation)
|
||||||
|
.attr("jid", match self.jid {
|
||||||
|
Some(jid) => Some(String::from(jid)),
|
||||||
|
None => None,
|
||||||
|
})
|
||||||
|
.attr("nick", self.nick)
|
||||||
|
.attr("role", self.role)
|
||||||
|
.append(self.actor)
|
||||||
|
.append(self.continue_)
|
||||||
|
.append(self.reason)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MucUser {
|
||||||
|
pub status: Vec<Status>,
|
||||||
|
pub items: Vec<Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Element> for MucUser {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(elem: Element) -> Result<MucUser, Error> {
|
||||||
|
if !elem.is("x", ns::MUC_USER) {
|
||||||
|
return Err(Error::ParseError("This is not an x element."));
|
||||||
|
}
|
||||||
|
let mut status = vec!();
|
||||||
|
let mut items = vec!();
|
||||||
|
for child in elem.children() {
|
||||||
|
if child.is("status", ns::MUC_USER) {
|
||||||
|
status.push(Status::try_from(child.clone())?);
|
||||||
|
} else if child.is("item", ns::MUC_USER) {
|
||||||
|
items.push(Item::try_from(child.clone())?);
|
||||||
|
} else {
|
||||||
|
return Err(Error::ParseError("Unknown child in x element."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _ in elem.attrs() {
|
||||||
|
return Err(Error::ParseError("Unknown attribute in x element."));
|
||||||
|
}
|
||||||
|
Ok(MucUser {
|
||||||
|
status,
|
||||||
|
items,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Element> for MucUser {
|
||||||
|
fn into(self) -> Element {
|
||||||
|
Element::builder("x")
|
||||||
|
.ns(ns::MUC_USER)
|
||||||
|
.append(self.status)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::error::Error as StdError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple() {
|
||||||
|
let elem: Element = "
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc#user'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
MucUser::try_from(elem).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_child() {
|
||||||
|
let elem: Element = "
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
|
<coucou/>
|
||||||
|
</x>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = MucUser::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Unknown child in x element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialise() {
|
||||||
|
let elem: Element = "
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc#user'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let muc = MucUser { status: vec!(), items: vec!() };
|
||||||
|
let elem2 = muc.into();
|
||||||
|
assert_eq!(elem, elem2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_attribute() {
|
||||||
|
let elem: Element = "
|
||||||
|
<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = MucUser::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Unknown attribute in x element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_simple() {
|
||||||
|
let elem: Element = "
|
||||||
|
<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
Status::try_from(elem).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_invalid() {
|
||||||
|
let elem: Element = "
|
||||||
|
<status xmlns='http://jabber.org/protocol/muc#user'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Status::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Required attribute 'code' missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_invalid_child() {
|
||||||
|
let elem: Element = "
|
||||||
|
<status xmlns='http://jabber.org/protocol/muc#user' code='110'>
|
||||||
|
<foo/>
|
||||||
|
</status>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Status::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => 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();
|
||||||
|
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();
|
||||||
|
let error = Status::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Invalid status code.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_invalid_code2() {
|
||||||
|
let elem: Element = "
|
||||||
|
<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Status::try_from(elem).unwrap_err();
|
||||||
|
let error = match error {
|
||||||
|
Error::ParseIntError(error) => error,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(error.description(), "invalid digit found in string");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_actor_required_attributes() {
|
||||||
|
let elem: Element = "
|
||||||
|
<actor xmlns='http://jabber.org/protocol/muc#user'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Actor::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => 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'
|
||||||
|
jid='foo@bar/baz'
|
||||||
|
nick='baz'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Actor::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => 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();
|
||||||
|
let actor = Actor::try_from(elem).unwrap();
|
||||||
|
let jid = match actor {
|
||||||
|
Actor::Jid(jid) => jid,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(jid, "foo@bar/baz".parse::<Jid>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_actor_nick() {
|
||||||
|
let elem: Element = "
|
||||||
|
<actor xmlns='http://jabber.org/protocol/muc#user' nick='baz'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
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();
|
||||||
|
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();
|
||||||
|
let continue_ = Continue::try_from(elem).unwrap();
|
||||||
|
assert_eq!(continue_, Continue { thread: Some("foo".to_owned()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_continue_invalid() {
|
||||||
|
let elem: Element = "
|
||||||
|
<continue xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
|
<foobar/>
|
||||||
|
</continue>
|
||||||
|
".parse().unwrap();
|
||||||
|
let continue_ = Continue::try_from(elem).unwrap_err();
|
||||||
|
let message = match continue_ {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Unknown child in continue element.".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reason_simple() {
|
||||||
|
let elem: Element = "
|
||||||
|
<reason xmlns='http://jabber.org/protocol/muc#user'>Reason</reason>"
|
||||||
|
.parse().unwrap();
|
||||||
|
let reason = Reason::try_from(elem).unwrap();
|
||||||
|
assert_eq!(reason.0, "Reason".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reason_invalid_attribute() {
|
||||||
|
let elem: Element = "
|
||||||
|
<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Reason::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Unknown attribute in reason element.".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reason_invalid() {
|
||||||
|
let elem: Element = "
|
||||||
|
<reason xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
|
<foobar/>
|
||||||
|
</reason>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Reason::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Unknown child in reason element.".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_item_invalid_attr(){
|
||||||
|
let elem: Element = "
|
||||||
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
|
foo='bar'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Item::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Unknown attribute in item element.".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_item_affiliation_role_attr(){
|
||||||
|
let elem: Element = "
|
||||||
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
|
affiliation='member'
|
||||||
|
role='moderator'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
Item::try_from(elem).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_item_affiliation_role_invalid_attr(){
|
||||||
|
let elem: Element = "
|
||||||
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
|
affiliation='member'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Item::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Required attribute 'role' missing.".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_item_nick_attr(){
|
||||||
|
let elem: Element = "
|
||||||
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
|
affiliation='member'
|
||||||
|
role='moderator'
|
||||||
|
nick='foobar'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let item = Item::try_from(elem).unwrap();
|
||||||
|
match item {
|
||||||
|
Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_item_affiliation_role_invalid_attr2(){
|
||||||
|
let elem: Element = "
|
||||||
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
|
role='moderator'/>
|
||||||
|
".parse().unwrap();
|
||||||
|
let error = Item::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(message, "Required attribute 'affiliation' missing.".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_item_role_actor_child(){
|
||||||
|
let elem: Element = "
|
||||||
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
|
affiliation='member'
|
||||||
|
role='moderator'>
|
||||||
|
<actor nick='foobar'/>
|
||||||
|
</item>
|
||||||
|
".parse().unwrap();
|
||||||
|
let item = Item::try_from(elem).unwrap();
|
||||||
|
match item {
|
||||||
|
Item { actor, .. } =>
|
||||||
|
assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_item_role_continue_child(){
|
||||||
|
let elem: Element = "
|
||||||
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
|
affiliation='member'
|
||||||
|
role='moderator'>
|
||||||
|
<continue thread='foobar'/>
|
||||||
|
</item>
|
||||||
|
".parse().unwrap();
|
||||||
|
let item = Item::try_from(elem).unwrap();
|
||||||
|
let continue_1 = Continue { thread: Some("foobar".to_owned()) };
|
||||||
|
match item {
|
||||||
|
Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2, continue_1),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_item_role_reason_child(){
|
||||||
|
let elem: Element = "
|
||||||
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
|
affiliation='member'
|
||||||
|
role='moderator'>
|
||||||
|
<reason>foobar</reason>
|
||||||
|
</item>
|
||||||
|
".parse().unwrap();
|
||||||
|
let item = Item::try_from(elem).unwrap();
|
||||||
|
match item {
|
||||||
|
Item { reason, .. } =>
|
||||||
|
assert_eq!(reason, Some(Reason("foobar".to_owned()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue