diff --git a/src/muc.rs b/src/muc.rs deleted file mode 100644 index c8248b75..00000000 --- a/src/muc.rs +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright (c) 2017 Maxime “pep” Buquet -// -// 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 for Muc { - type Error = Error; - - fn try_from(elem: Element) -> Result { - 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 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 for Status { - type Error = Error; - - fn try_from(elem: Element) -> Result { - 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 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, -} - -impl TryFrom for MucUser { - type Error = Error; - - fn try_from(elem: Element) -> Result { - 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 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 = "".parse().unwrap(); - Muc::try_from(elem).unwrap(); - } - - #[test] - fn test_muc_invalid_child() { - let elem: Element = "".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 = "".parse().unwrap(); - let muc = Muc; - let elem2 = muc.into(); - assert_eq!(elem, elem2); - } - - #[test] - fn test_muc_invalid_attribute() { - let elem: Element = "".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 = "".parse().unwrap(); - MucUser::try_from(elem).unwrap(); - } - - #[test] - fn test_muc_user_invalid_child() { - let elem: Element = "".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 = "".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 = "".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 = "".parse().unwrap(); - Status::try_from(elem).unwrap(); - } - - #[test] - fn test_status_invalid() { - let elem: Element = "".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 = "".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 = "".parse().unwrap(); - let status = Status::try_from(elem).unwrap(); - assert_eq!(status, Status::Kicked); - } - - #[test] - fn test_status_invalid_code() { - let elem: Element = "".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 = "".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"); - } -} diff --git a/src/muc/mod.rs b/src/muc/mod.rs new file mode 100644 index 00000000..80d61d88 --- /dev/null +++ b/src/muc/mod.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// 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; diff --git a/src/muc/muc.rs b/src/muc/muc.rs new file mode 100644 index 00000000..75667d87 --- /dev/null +++ b/src/muc/muc.rs @@ -0,0 +1,108 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// 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, +} + +impl TryFrom for Muc { + type Error = Error; + + fn try_from(elem: Element) -> Result { + 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 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 = "".parse().unwrap(); + Muc::try_from(elem).unwrap(); + } + + #[test] + fn test_muc_invalid_child() { + let elem: Element = "".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 = "".parse().unwrap(); + let muc = Muc { + password: None, + }; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_muc_invalid_attribute() { + let elem: Element = "".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 = " + + coucou + " + .parse().unwrap(); + let muc = Muc::try_from(elem).unwrap(); + assert_eq!(muc.password, Some("coucou".to_owned())); + } +} diff --git a/src/muc/user.rs b/src/muc/user.rs new file mode 100644 index 00000000..8dae704a --- /dev/null +++ b/src/muc/user.rs @@ -0,0 +1,830 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// 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 for Status { + type Error = Error; + + fn try_from(elem: Element) -> Result { + 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 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 element used in 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 for Actor { + type Error = Error; + + fn try_from(elem: Element) -> Result { + 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 = 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 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, +} + +impl TryFrom for Continue { + type Error = Error; + + fn try_from(elem: Element) -> Result { + 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 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 for Reason { + type Error = Error; + + fn try_from(elem: Element) -> Result { + 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 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 { + 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 { + 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 { + 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 { + 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, + pub nick: Option, + pub role: Role, + pub actor: Option, + pub continue_: Option, + pub reason: Option, +} + +impl TryFrom for Item { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("item", ns::MUC_USER) { + return Err(Error::ParseError("This is not a item element.")); + } + let mut actor: Option = None; + let mut continue_: Option = None; + let mut reason: Option = 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 = get_attr!(elem, "jid", optional); + let nick: Option = 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 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, + pub items: Vec, +} + +impl TryFrom for MucUser { + type Error = Error; + + fn try_from(elem: Element) -> Result { + 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 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 = " + + ".parse().unwrap(); + MucUser::try_from(elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = " + + + + ".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 = " + + ".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 = " + + ".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 = " + + ".parse().unwrap(); + Status::try_from(elem).unwrap(); + } + + #[test] + fn test_status_invalid() { + let elem: Element = " + + ".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 = " + + + + ".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 = " + + ".parse().unwrap(); + let status = Status::try_from(elem).unwrap(); + assert_eq!(status, Status::Kicked); + } + + #[test] + fn test_status_invalid_code() { + let elem: Element = " + + ".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 = " + + ".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 = " + + ".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 = " + + ".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 = " + + ".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::().unwrap()); + } + + #[test] + fn test_actor_nick() { + let elem: Element = " + + ".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 = " + + ".parse().unwrap(); + Continue::try_from(elem).unwrap(); + } + + #[test] + fn test_continue_thread_attribute() { + let elem: Element = " + + ".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 = " + + + + ".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" + .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 = " + + ".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 = " + + + + ".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 = " + + ".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 = " + + ".parse().unwrap(); + Item::try_from(elem).unwrap(); + } + + #[test] + fn test_item_affiliation_role_invalid_attr(){ + let elem: Element = " + + ".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 = " + + ".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 = " + + ".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 = " + + + + ".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 = " + + + + ".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 = " + + foobar + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + match item { + Item { reason, .. } => + assert_eq!(reason, Some(Reason("foobar".to_owned()))), + } + } +}