Add a generate_attribute! macro, and use it for the common case.

Emmanuel Gil Peyrot created

Change summary

src/data_forms.rs   | 102 +++++-----------------------
src/ibb.rs          |  37 +---------
src/jingle.rs       | 165 +++++++++-------------------------------------
src/jingle_s5b.rs   |  82 ++--------------------
src/lib.rs          |  60 +++++++++++++++++
src/mam.rs          |  35 +--------
src/message.rs      |  43 +----------
src/pubsub/event.rs |  45 +-----------
src/roster.rs       |  43 +----------
src/stanza_error.rs |  45 ++----------
10 files changed, 158 insertions(+), 499 deletions(-)

Detailed changes

src/data_forms.rs 🔗

@@ -14,19 +14,18 @@ use ns;
 
 use media_element::MediaElement;
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum FieldType {
-    Boolean,
-    Fixed,
-    Hidden,
-    JidMulti,
-    JidSingle,
-    ListMulti,
-    ListSingle,
-    TextMulti,
-    TextPrivate,
-    TextSingle,
-}
+generate_attribute!(FieldType, "type", {
+    Boolean => "boolean",
+    Fixed => "fixed",
+    Hidden => "hidden",
+    JidMulti => "jid-multi",
+    JidSingle => "jid-single",
+    ListMulti => "list-multi",
+    ListSingle => "list-single",
+    TextMulti => "text-multi",
+    TextPrivate => "text-private",
+    TextSingle => "text-single",
+});
 
 impl Default for FieldType {
     fn default() -> FieldType {
@@ -34,44 +33,6 @@ impl Default for FieldType {
     }
 }
 
-impl FromStr for FieldType {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<FieldType, Error> {
-        Ok(match s {
-            "boolean" => FieldType::Boolean,
-            "fixed" => FieldType::Fixed,
-            "hidden" => FieldType::Hidden,
-            "jid-multi" => FieldType::JidMulti,
-            "jid-single" => FieldType::JidSingle,
-            "list-multi" => FieldType::ListMulti,
-            "list-single" => FieldType::ListSingle,
-            "text-multi" => FieldType::TextMulti,
-            "text-private" => FieldType::TextPrivate,
-            "text-single" => FieldType::TextSingle,
-
-            _ => return Err(Error::ParseError("Invalid 'type' attribute in field element.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for FieldType {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            FieldType::Boolean => "boolean",
-            FieldType::Fixed => "fixed",
-            FieldType::Hidden => "hidden",
-            FieldType::JidMulti => "jid-multi",
-            FieldType::JidSingle => "jid-single",
-            FieldType::ListMulti => "list-multi",
-            FieldType::ListSingle => "list-single",
-            FieldType::TextMulti => "text-multi",
-            FieldType::TextPrivate => "text-private",
-            FieldType::TextSingle => "text-single",
-        }))
-    }
-}
-
 #[derive(Debug, Clone)]
 pub struct Option_ {
     pub label: Option<String>,
@@ -131,39 +92,12 @@ impl IntoElements for Field {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum DataFormType {
-    Cancel,
-    Form,
-    Result_,
-    Submit,
-}
-
-impl FromStr for DataFormType {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<DataFormType, Error> {
-        Ok(match s {
-            "cancel" => DataFormType::Cancel,
-            "form" => DataFormType::Form,
-            "result" => DataFormType::Result_,
-            "submit" => DataFormType::Submit,
-
-            _ => return Err(Error::ParseError("Unknown data form type.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for DataFormType {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            DataFormType::Cancel => "cancel",
-            DataFormType::Form => "form",
-            DataFormType::Result_ => "result",
-            DataFormType::Submit => "submit",
-        }))
-    }
-}
+generate_attribute!(Subscription, "subscription", {
+    Cancel => "cancel",
+    Form => "form",
+    Result_ => "result",
+    Submit => "submit",
+});
 
 #[derive(Debug, Clone)]
 pub struct DataForm {

src/ibb.rs 🔗

@@ -14,39 +14,10 @@ use error::Error;
 
 use ns;
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum Stanza {
-    Iq,
-    Message,
-}
-
-impl Default for Stanza {
-    fn default() -> Stanza {
-        Stanza::Iq
-    }
-}
-
-impl FromStr for Stanza {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Stanza, Error> {
-        Ok(match s {
-            "iq" => Stanza::Iq,
-            "message" => Stanza::Message,
-
-            _ => return Err(Error::ParseError("Invalid 'stanza' attribute.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for Stanza {
-    fn into_attribute_value(self) -> Option<String> {
-        match self {
-            Stanza::Iq => None,
-            Stanza::Message => Some(String::from("message")),
-        }
-    }
-}
+generate_attribute!(Stanza, "stanza", {
+    Iq => "iq",
+    Message => "message",
+}, Default = Iq);
 
 #[derive(Debug, Clone)]
 pub enum IBB {

src/jingle.rs 🔗

@@ -13,108 +13,35 @@ use jid::Jid;
 use error::Error;
 use ns;
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum Action {
-    ContentAccept,
-    ContentAdd,
-    ContentModify,
-    ContentReject,
-    ContentRemove,
-    DescriptionInfo,
-    SecurityInfo,
-    SessionAccept,
-    SessionInfo,
-    SessionInitiate,
-    SessionTerminate,
-    TransportAccept,
-    TransportInfo,
-    TransportReject,
-    TransportReplace,
-}
-
-impl FromStr for Action {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Action, Error> {
-        Ok(match s {
-            "content-accept" => Action::ContentAccept,
-            "content-add" => Action::ContentAdd,
-            "content-modify" => Action::ContentModify,
-            "content-reject" => Action::ContentReject,
-            "content-remove" => Action::ContentRemove,
-            "description-info" => Action::DescriptionInfo,
-            "security-info" => Action::SecurityInfo,
-            "session-accept" => Action::SessionAccept,
-            "session-info" => Action::SessionInfo,
-            "session-initiate" => Action::SessionInitiate,
-            "session-terminate" => Action::SessionTerminate,
-            "transport-accept" => Action::TransportAccept,
-            "transport-info" => Action::TransportInfo,
-            "transport-reject" => Action::TransportReject,
-            "transport-replace" => Action::TransportReplace,
-
-            _ => return Err(Error::ParseError("Unknown action.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for Action {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            Action::ContentAccept => "content-accept",
-            Action::ContentAdd => "content-add",
-            Action::ContentModify => "content-modify",
-            Action::ContentReject => "content-reject",
-            Action::ContentRemove => "content-remove",
-            Action::DescriptionInfo => "description-info",
-            Action::SecurityInfo => "security-info",
-            Action::SessionAccept => "session-accept",
-            Action::SessionInfo => "session-info",
-            Action::SessionInitiate => "session-initiate",
-            Action::SessionTerminate => "session-terminate",
-            Action::TransportAccept => "transport-accept",
-            Action::TransportInfo => "transport-info",
-            Action::TransportReject => "transport-reject",
-            Action::TransportReplace => "transport-replace",
-        }))
-    }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Creator {
-    Initiator,
-    Responder,
-}
-
-impl FromStr for Creator {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Creator, Error> {
-        Ok(match s {
-            "initiator" => Creator::Initiator,
-            "responder" => Creator::Responder,
-
-            _ => return Err(Error::ParseError("Unknown creator.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for Creator {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            Creator::Initiator => "initiator",
-            Creator::Responder => "responder",
-        }))
-    }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Senders {
-    Both,
-    Initiator,
-    None_,
-    Responder,
-}
+generate_attribute!(Action, "action", {
+    ContentAccept => "content-accept",
+    ContentAdd => "content-add",
+    ContentModify => "content-modify",
+    ContentReject => "content-reject",
+    ContentRemove => "content-remove",
+    DescriptionInfo => "description-info",
+    SecurityInfo => "security-info",
+    SessionAccept => "session-accept",
+    SessionInfo => "session-info",
+    SessionInitiate => "session-initiate",
+    SessionTerminate => "session-terminate",
+    TransportAccept => "transport-accept",
+    TransportInfo => "transport-info",
+    TransportReject => "transport-reject",
+    TransportReplace => "transport-replace",
+});
+
+generate_attribute!(Creator, "creator", {
+    Initiator => "initiator",
+    Responder => "responder",
+});
+
+generate_attribute!(Senders, "senders", {
+    Both => "both",
+    Initiator => "initiator",
+    None => "none",
+    Responder => "responder",
+});
 
 impl Default for Senders {
     fn default() -> Senders {
@@ -122,32 +49,6 @@ impl Default for Senders {
     }
 }
 
-impl FromStr for Senders {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Senders, Error> {
-        Ok(match s {
-            "both" => Senders::Both,
-            "initiator" => Senders::Initiator,
-            "none" => Senders::None_,
-            "responder" => Senders::Responder,
-
-            _ => return Err(Error::ParseError("Unknown senders.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for Senders {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            Senders::Both => "both",
-            Senders::Initiator => "initiator",
-            Senders::None_ => "none",
-            Senders::Responder => "responder",
-        }))
-    }
-}
-
 #[derive(Debug, Clone)]
 pub struct Content {
     pub creator: Creator,
@@ -448,7 +349,7 @@ mod tests {
             Error::ParseError(string) => string,
             _ => panic!(),
         };
-        assert_eq!(message, "Unknown action.");
+        assert_eq!(message, "Unknown value for 'action' attribute.");
     }
 
     #[test]
@@ -493,7 +394,7 @@ mod tests {
             Error::ParseError(string) => string,
             _ => panic!(),
         };
-        assert_eq!(message, "Unknown creator.");
+        assert_eq!(message, "Unknown value for 'creator' attribute.");
 
         let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='initiator' name='coucou' senders='coucou'/></jingle>".parse().unwrap();
         let error = Jingle::try_from(elem).unwrap_err();
@@ -501,7 +402,7 @@ mod tests {
             Error::ParseError(string) => string,
             _ => panic!(),
         };
-        assert_eq!(message, "Unknown senders.");
+        assert_eq!(message, "Unknown value for 'senders' attribute.");
 
         let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='initiator' name='coucou' senders=''/></jingle>".parse().unwrap();
         let error = Jingle::try_from(elem).unwrap_err();
@@ -509,7 +410,7 @@ mod tests {
             Error::ParseError(string) => string,
             _ => panic!(),
         };
-        assert_eq!(message, "Unknown senders.");
+        assert_eq!(message, "Unknown value for 'senders' attribute.");
     }
 
     #[test]

src/jingle_s5b.rs 🔗

@@ -13,45 +13,12 @@ use error::Error;
 
 use ns;
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum Type {
-    Assisted,
-    Direct,
-    Proxy,
-    Tunnel,
-}
-
-impl Default for Type {
-    fn default() -> Type {
-        Type::Direct
-    }
-}
-
-impl FromStr for Type {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Type, Error> {
-        Ok(match s {
-            "assisted" => Type::Assisted,
-            "direct" => Type::Direct,
-            "proxy" => Type::Proxy,
-            "tunnel" => Type::Tunnel,
-
-            _ => return Err(Error::ParseError("Invalid 'type' attribute in candidate element.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for Type {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(match self {
-            Type::Assisted => String::from("assisted"),
-            Type::Direct => return None,
-            Type::Proxy => String::from("proxy"),
-            Type::Tunnel => String::from("tunnel"),
-        })
-    }
-}
+generate_attribute!(Type, "type", {
+    Assisted => "assisted",
+    Direct => "direct",
+    Proxy => "proxy",
+    Tunnel => "tunnel",
+}, Default = Direct);
 
 #[derive(Debug, Clone)]
 pub struct Candidate {
@@ -77,39 +44,10 @@ impl Into<Element> for Candidate {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum Mode {
-    Tcp,
-    Udp,
-}
-
-impl Default for Mode {
-    fn default() -> Mode {
-        Mode::Tcp
-    }
-}
-
-impl FromStr for Mode {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Mode, Error> {
-        Ok(match s {
-            "tcp" => Mode::Tcp,
-            "udp" => Mode::Udp,
-
-            _ => return Err(Error::ParseError("Invalid 'mode' attribute.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for Mode {
-    fn into_attribute_value(self) -> Option<String> {
-        match self {
-            Mode::Tcp => None,
-            Mode::Udp => Some(String::from("udp")),
-        }
-    }
-}
+generate_attribute!(Mode, "mode", {
+    Tcp => "tcp",
+    Udp => "udp",
+}, Default = Tcp);
 
 #[derive(Debug, Clone)]
 pub enum TransportPayload {

src/lib.rs 🔗

@@ -49,6 +49,66 @@ macro_rules! get_attr {
     );
 }
 
+macro_rules! generate_attribute {
+    ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => (
+        generate_attribute!($elem, $name, {$($a => $b),+});
+    );
+    ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => (
+        generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default);
+    );
+    ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => (
+        #[derive(Debug, Clone, PartialEq)]
+        pub enum $elem {
+            $($a),+
+        }
+        impl FromStr for $elem {
+            type Err = Error;
+            fn from_str(s: &str) -> Result<$elem, Error> {
+                Ok(match s {
+                    $($b => $elem::$a),+,
+                    _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
+                })
+            }
+        }
+        impl IntoAttributeValue for $elem {
+            fn into_attribute_value(self) -> Option<String> {
+                Some(String::from(match self {
+                    $($elem::$a => $b),+
+                }))
+            }
+        }
+    );
+    ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => (
+        #[derive(Debug, Clone, PartialEq)]
+        pub enum $elem {
+            $($a),+
+        }
+        impl FromStr for $elem {
+            type Err = Error;
+            fn from_str(s: &str) -> Result<$elem, Error> {
+                Ok(match s {
+                    $($b => $elem::$a),+,
+                    _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
+                })
+            }
+        }
+        impl IntoAttributeValue for $elem {
+            #[allow(unreachable_patterns)]
+            fn into_attribute_value(self) -> Option<String> {
+                Some(String::from(match self {
+                    $elem::$default => return None,
+                    $($elem::$a => $b),+
+                }))
+            }
+        }
+        impl Default for $elem {
+            fn default() -> $elem {
+                $elem::$default
+            }
+        }
+    );
+}
+
 /// Error type returned by every parser on failure.
 pub mod error;
 /// XML namespace definitions used through XMPP.

src/mam.rs 🔗

@@ -39,36 +39,11 @@ pub struct Fin {
     pub set: Set,
 }
 
-#[derive(Debug, Clone)]
-pub enum DefaultPrefs {
-    Always,
-    Never,
-    Roster,
-}
-
-impl FromStr for DefaultPrefs {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<DefaultPrefs, Error> {
-        Ok(match s {
-            "always" => DefaultPrefs::Always,
-            "never" => DefaultPrefs::Never,
-            "roster" => DefaultPrefs::Roster,
-
-            _ => return Err(Error::ParseError("Invalid 'default' attribute.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for DefaultPrefs {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            DefaultPrefs::Always => "always",
-            DefaultPrefs::Never => "never",
-            DefaultPrefs::Roster => "roster",
-        }))
-    }
-}
+generate_attribute!(DefaultPrefs, "default", {
+    Always => "always",
+    Never => "never",
+    Roster => "roster",
+});
 
 #[derive(Debug, Clone)]
 pub struct Prefs {

src/message.rs 🔗

@@ -102,14 +102,13 @@ impl Into<Element> for MessagePayload {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum MessageType {
-    Chat,
-    Error,
-    Groupchat,
-    Headline,
-    Normal,
-}
+generate_attribute!(MessageType, "type", {
+    Chat => "chat",
+    Error => "error",
+    Groupchat => "groupchat",
+    Headline => "headline",
+    Normal => "normal",
+});
 
 impl Default for MessageType {
     fn default() -> MessageType {
@@ -117,34 +116,6 @@ impl Default for MessageType {
     }
 }
 
-impl FromStr for MessageType {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<MessageType, Error> {
-        Ok(match s {
-            "chat" => MessageType::Chat,
-            "error" => MessageType::Error,
-            "groupchat" => MessageType::Groupchat,
-            "headline" => MessageType::Headline,
-            "normal" => MessageType::Normal,
-
-            _ => return Err(Error::ParseError("Invalid 'type' attribute on message element.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for MessageType {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(match self {
-            MessageType::Chat => "chat",
-            MessageType::Error => "error",
-            MessageType::Groupchat => "groupchat",
-            MessageType::Headline => "headline",
-            MessageType::Normal => "normal",
-        }.to_owned())
-    }
-}
-
 type Lang = String;
 type Body = String;
 type Subject = String;

src/pubsub/event.rs 🔗

@@ -43,45 +43,12 @@ impl IntoElements for Item {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum Subscription {
-    None,
-    Pending,
-    Subscribed,
-    Unconfigured,
-}
-
-impl Default for Subscription {
-    fn default() -> Subscription {
-        Subscription::None
-    }
-}
-
-impl FromStr for Subscription {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Subscription, Error> {
-        Ok(match s {
-            "none" => Subscription::None,
-            "pending" => Subscription::Pending,
-            "subscribed" => Subscription::Subscribed,
-            "unconfigured" => Subscription::Unconfigured,
-
-            _ => return Err(Error::ParseError("Invalid 'subscription' attribute.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for Subscription {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            Subscription::None => return None,
-            Subscription::Pending => "pending",
-            Subscription::Subscribed => "subscribed",
-            Subscription::Unconfigured => "unconfigured",
-        }))
-    }
-}
+generate_attribute!(Subscription, "subscription", {
+    None => "none",
+    Pending => "pending",
+    Subscribed => "subscribed",
+    Unconfigured => "unconfigured",
+}, Default = None);
 
 #[derive(Debug, Clone)]
 pub enum PubSubEvent {

src/roster.rs 🔗

@@ -15,42 +15,13 @@ use ns;
 
 type Group = String;
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum Subscription {
-    None,
-    From,
-    To,
-    Both,
-    Remove,
-}
-
-impl FromStr for Subscription {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Subscription, Error> {
-        Ok(match s {
-            "none" => Subscription::None,
-            "from" => Subscription::From,
-            "to" => Subscription::To,
-            "both" => Subscription::Both,
-            "remove" => Subscription::Remove,
-
-            _ => return Err(Error::ParseError("Unknown value for attribute 'subscription'.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for Subscription {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            Subscription::None => "none",
-            Subscription::From => "from",
-            Subscription::To => "to",
-            Subscription::Both => "both",
-            Subscription::Remove => "remove",
-        }))
-    }
-}
+generate_attribute!(Subscription, "subscription", {
+    None => "none",
+    From => "from",
+    To => "to",
+    Both => "both",
+    Remove => "remove",
+});
 
 #[derive(Debug, Clone, PartialEq)]
 pub struct Item {

src/stanza_error.rs 🔗

@@ -14,42 +14,13 @@ use error::Error;
 use jid::Jid;
 use ns;
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum ErrorType {
-    Auth,
-    Cancel,
-    Continue,
-    Modify,
-    Wait,
-}
-
-impl FromStr for ErrorType {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<ErrorType, Error> {
-        Ok(match s {
-            "auth" => ErrorType::Auth,
-            "cancel" => ErrorType::Cancel,
-            "continue" => ErrorType::Continue,
-            "modify" => ErrorType::Modify,
-            "wait" => ErrorType::Wait,
-
-            _ => return Err(Error::ParseError("Unknown error type.")),
-        })
-    }
-}
-
-impl IntoAttributeValue for ErrorType {
-    fn into_attribute_value(self) -> Option<String> {
-        Some(String::from(match self {
-            ErrorType::Auth => "auth",
-            ErrorType::Cancel => "cancel",
-            ErrorType::Continue => "continue",
-            ErrorType::Modify => "modify",
-            ErrorType::Wait => "wait",
-        }))
-    }
-}
+generate_attribute!(ErrorType, "type", {
+    Auth => "auth",
+    Cancel => "cancel",
+    Continue => "continue",
+    Modify => "modify",
+    Wait => "wait",
+});
 
 #[derive(Debug, Clone, PartialEq)]
 pub enum DefinedCondition {
@@ -252,7 +223,7 @@ mod tests {
             Error::ParseError(string) => string,
             _ => panic!(),
         };
-        assert_eq!(message, "Unknown error type.");
+        assert_eq!(message, "Unknown value for 'type' attribute.");
     }
 
     #[test]