muc/user: Simplify Status with a new macro.

Emmanuel Gil Peyrot created

Change summary

src/macros.rs   |  38 +++++++++++++++++
src/muc/user.rs | 112 +++++++++-----------------------------------------
2 files changed, 59 insertions(+), 91 deletions(-)

Detailed changes

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<Element> 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);

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<Element> for Status {
-    type Err = 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 From<Status> 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 <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
@@ -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]