diff --git a/src/macros.rs b/src/macros.rs index b084207..8fb5736 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -139,6 +139,44 @@ macro_rules! generate_element_enum { ); } +macro_rules! generate_attribute_enum { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+}); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + $(#[$enum_meta])* + $enum + ),+ + } + impl TryFrom for $elem { + type Err = Error; + fn try_from(elem: Element) -> Result<$elem, Error> { + check_ns_only!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$attr]); + Ok(match get_attr!(elem, $attr, required) { + $($enum_name => $elem::$enum,)+ + _ => return Err(Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + }) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + .attr($attr, match elem { + $($elem::$enum => $enum_name,)+ + }) + .build() + } + } + ); +} + macro_rules! check_self { ($elem:ident, $name:tt, $ns:expr) => ( check_self!($elem, $name, $ns, $name); diff --git a/src/muc/user.rs b/src/muc/user.rs index 6c25209..deff945 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -16,131 +16,61 @@ use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Status { +generate_attribute_enum!(Status, "status", ns::MUC_USER, "code", { /// Status: 100 - NonAnonymousRoom, + NonAnonymousRoom => 100, /// Status: 101 - AffiliationChange, + AffiliationChange => 101, /// Status: 102 - ConfigShowsUnavailableMembers, + ConfigShowsUnavailableMembers => 102, /// Status: 103 - ConfigHidesUnavailableMembers, + ConfigHidesUnavailableMembers => 103, /// Status: 104 - ConfigNonPrivacyRelated, + ConfigNonPrivacyRelated => 104, /// Status: 110 - SelfPresence, + SelfPresence => 110, /// Status: 170 - ConfigRoomLoggingEnabled, + ConfigRoomLoggingEnabled => 170, /// Status: 171 - ConfigRoomLoggingDisabled, + ConfigRoomLoggingDisabled => 171, /// Status: 172 - ConfigRoomNonAnonymous, + ConfigRoomNonAnonymous => 172, /// Status: 173 - ConfigRoomSemiAnonymous, + ConfigRoomSemiAnonymous => 173, /// Status: 201 - RoomHasBeenCreated, + RoomHasBeenCreated => 201, /// Status: 210 - AssignedNick, + AssignedNick => 210, /// Status: 301 - Banned, + Banned => 301, /// Status: 303 - NewNick, + NewNick => 303, /// Status: 307 - Kicked, + Kicked => 307, /// Status: 321 - RemovalFromRoom, + RemovalFromRoom => 321, /// Status: 322 - ConfigMembersOnly, + ConfigMembersOnly => 322, /// Status: 332 - ServiceShutdown, -} - -impl TryFrom for Status { - type Err = 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 From for Element { - fn from(status: Status) -> Element { - Element::builder("status") - .ns(ns::MUC_USER) - .attr("code", match status { - 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() - } -} + ServiceShutdown => 332, +}); /// 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 @@ -434,7 +364,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Invalid status code."); + assert_eq!(message, "Invalid status code value."); } #[test]