macros: Use a nicer syntax when declaring attributes.

Emmanuel Gil Peyrot created

The previous version had a => required|optional|default token, this was
duplicating information for Option types and didn’t look very good.

This new version looks like a type, which can be either Required<_>,
Option<_> or Default<_>, and means the same thing.

Change summary

src/avatar.rs          | 12 ++++----
src/bookmarks.rs       | 10 ++++----
src/data_forms.rs      |  2 
src/delay.rs           |  4 +-
src/disco.rs           | 14 +++++-----
src/eme.rs             |  4 +-
src/hashes.rs          |  2 
src/ibb.rs             | 12 ++++----
src/idle.rs            |  2 
src/jingle.rs          |  8 +++---
src/jingle_ft.rs       |  8 +++---
src/jingle_ibb.rs      |  6 ++--
src/jingle_s5b.rs      | 12 ++++----
src/mam.rs             | 10 ++++----
src/media_element.rs   |  6 ++--
src/message_correct.rs |  2 
src/muc/muc.rs         |  8 +++---
src/muc/user.rs        | 10 ++++----
src/pubsub/pubsub.rs   | 50 ++++++++++++++++++++--------------------
src/receipts.rs        |  2 
src/roster.rs          | 10 ++++----
src/sasl.rs            |  2 
src/sm.rs              | 24 +++++++++---------
src/stanza_id.rs       |  6 ++--
src/stream.rs          | 10 ++++----
src/util/macros.rs     | 53 ++++++++++++++++++++++++++++++++-----------
src/websocket.rs       | 10 ++++----
27 files changed, 162 insertions(+), 137 deletions(-)

Detailed changes

src/avatar.rs πŸ”—

@@ -24,22 +24,22 @@ generate_element!(
     Info, "info", AVATAR_METADATA,
     attributes: [
         /// The size of the image data in bytes.
-        bytes: u16 = "bytes" => required,
+        bytes: Required<u16> = "bytes",
 
         /// The width of the image in pixels.
-        width: Option<u8> = "width" => optional,
+        width: Option<u8> = "width",
 
         /// The height of the image in pixels.
-        height: Option<u8> = "height" => optional,
+        height: Option<u8> = "height",
 
         /// The SHA-1 hash of the image data for the specified content-type.
-        id: Sha1HexAttribute = "id" => required,
+        id: Required<Sha1HexAttribute> = "id",
 
         /// The IANA-registered content type of the image data.
-        type_: String = "type" => required,
+        type_: Required<String> = "type",
 
         /// The http: or https: URL at which the image data file is hosted.
-        url: Option<String> = "url" => optional,
+        url: Option<String> = "url",
     ]
 );
 

src/bookmarks.rs πŸ”—

@@ -18,13 +18,13 @@ generate_element!(
     Conference, "conference", BOOKMARKS,
     attributes: [
         /// Whether a conference bookmark should be joined automatically.
-        autojoin: Autojoin = "autojoin" => default,
+        autojoin: Default<Autojoin> = "autojoin",
 
         /// The JID of the conference.
-        jid: Jid = "jid" => required,
+        jid: Required<Jid> = "jid",
 
         /// A user-defined name for this conference.
-        name: String = "name" => required
+        name: Required<String> = "name",
     ],
     children: [
         /// The nick the user will use to join this conference.
@@ -40,10 +40,10 @@ generate_element!(
     Url, "url", BOOKMARKS,
     attributes: [
         /// A user-defined name for this URL.
-        name: String = "name" => required,
+        name: Required<String> = "name",
 
         /// The URL of this bookmark.
-        url: String = "url" => required
+        url: Required<String> = "url",
     ]
 );
 

src/data_forms.rs πŸ”—

@@ -15,7 +15,7 @@ generate_element!(
     Option_, "option", DATA_FORMS,
     attributes: [
         /// The optional label to be displayed to the user for this option.
-        label: Option<String> = "label" => optional
+        label: Option<String> = "label"
     ],
     children: [
         /// The value returned to the server when selecting this option.

src/delay.rs πŸ”—

@@ -15,10 +15,10 @@ generate_element!(
     Delay, "delay", DELAY,
     attributes: [
         /// The entity which delayed this message.
-        from: Option<Jid> = "from" => optional,
+        from: Option<Jid> = "from",
 
         /// The time at which this message got stored.
-        stamp: DateTime = "stamp" => required
+        stamp: Required<DateTime> = "stamp"
     ],
     text: (
         /// The optional reason this message got delayed.

src/disco.rs πŸ”—

@@ -20,7 +20,7 @@ generate_element!(
 DiscoInfoQuery, "query", DISCO_INFO,
 attributes: [
     /// Node on which we are doing the discovery.
-    node: Option<String> = "node" => optional,
+    node: Option<String> = "node",
 ]);
 
 impl IqGetPayload for DiscoInfoQuery {}
@@ -31,7 +31,7 @@ generate_element!(
 Feature, "feature", DISCO_INFO,
 attributes: [
     /// Namespace of the feature we want to represent.
-    var: String = "var" => required,
+    var: Required<String> = "var",
 ]);
 
 impl Feature {
@@ -206,7 +206,7 @@ generate_element!(
 DiscoItemsQuery, "query", DISCO_ITEMS,
 attributes: [
     /// Node on which we are doing the discovery.
-    node: Option<String> = "node" => optional,
+    node: Option<String> = "node",
 ]);
 
 impl IqGetPayload for DiscoItemsQuery {}
@@ -216,11 +216,11 @@ generate_element!(
 Item, "item", DISCO_ITEMS,
 attributes: [
     /// JID of the entity pointed by this item.
-    jid: Jid = "jid" => required,
+    jid: Required<Jid> = "jid",
     /// Node of the entity pointed by this item.
-    node: Option<String> = "node" => optional,
+    node: Option<String> = "node",
     /// Name of the entity pointed by this item.
-    name: Option<String> = "name" => optional,
+    name: Option<String> = "name",
 ]);
 
 generate_element!(
@@ -232,7 +232,7 @@ generate_element!(
     DiscoItemsResult, "query", DISCO_ITEMS,
     attributes: [
         /// Node on which we have done this discovery.
-        node: Option<String> = "node" => optional
+        node: Option<String> = "node"
     ],
     children: [
         /// List of items pointed by this entity.

src/eme.rs πŸ”—

@@ -11,11 +11,11 @@ generate_element!(
     ExplicitMessageEncryption, "encryption", EME,
     attributes: [
         /// Namespace of the encryption scheme used.
-        namespace: String = "namespace" => required,
+        namespace: Required<String> = "namespace",
 
         /// User-friendly name for the encryption scheme, should be `None` for OTR,
         /// legacy OpenPGP and OX.
-        name: Option<String> = "name" => optional,
+        name: Option<String> = "name",
     ]
 );
 

src/hashes.rs πŸ”—

@@ -102,7 +102,7 @@ generate_element!(
     Hash, "hash", HASHES,
     attributes: [
         /// The algorithm used to create this hash.
-        algo: Algo = "algo" => required
+        algo: Required<Algo> = "algo"
     ],
     text: (
         /// The hash value, as a vector of bytes.

src/ibb.rs πŸ”—

@@ -30,13 +30,13 @@ generate_element!(
 Open, "open", IBB,
 attributes: [
     /// Maximum size in bytes for each chunk.
-    block_size: u16 = "block-size" => required,
+    block_size: Required<u16> = "block-size",
 
     /// The identifier to be used to create a stream.
-    sid: StreamId = "sid" => required,
+    sid: Required<StreamId> = "sid",
 
     /// Which stanza type to use to exchange data.
-    stanza: Stanza = "stanza" => default,
+    stanza: Default<Stanza> = "stanza",
 ]);
 
 impl IqSetPayload for Open {}
@@ -46,10 +46,10 @@ generate_element!(
 Data, "data", IBB,
     attributes: [
         /// Sequence number of this chunk, must wraparound after 65535.
-        seq: u16 = "seq" => required,
+        seq: Required<u16> = "seq",
 
         /// The identifier of the stream on which data is being exchanged.
-        sid: StreamId = "sid" => required
+        sid: Required<StreamId> = "sid"
     ],
     text: (
         /// Vector of bytes to be exchanged.
@@ -64,7 +64,7 @@ generate_element!(
 Close, "close", IBB,
 attributes: [
     /// The identifier of the stream to be closed.
-    sid: StreamId = "sid" => required,
+    sid: Required<StreamId> = "sid",
 ]);
 
 impl IqSetPayload for Close {}

src/idle.rs πŸ”—

@@ -12,7 +12,7 @@ generate_element!(
     Idle, "idle", IDLE,
     attributes: [
         /// The time at which the user stopped interacting.
-        since: DateTime = "since" => required,
+        since: Required<DateTime> = "since",
     ]
 );
 

src/jingle.rs πŸ”—

@@ -169,16 +169,16 @@ generate_element!(
     Content, "content", JINGLE,
     attributes: [
         /// Who created this content.
-        creator: Creator = "creator" => required,
+        creator: Required<Creator> = "creator",
 
         /// How the content definition is to be interpreted by the recipient.
-        disposition: Disposition = "disposition" => default,
+        disposition: Default<Disposition> = "disposition",
 
         /// A per-session unique identifier for this content.
-        name: ContentId = "name" => required,
+        name: Required<ContentId> = "name",
 
         /// Who can send data for this content.
-        senders: Senders = "senders" => default
+        senders: Default<Senders> = "senders",
     ],
     children: [
         /// What to send.

src/jingle_ft.rs πŸ”—

@@ -20,11 +20,11 @@ generate_element!(
     Range, "range", JINGLE_FT,
     attributes: [
         /// The offset in bytes from the beginning of the file.
-        offset: u64 = "offset" => default,
+        offset: Default<u64> = "offset",
 
         /// The length in bytes of the range, or None to be the entire
         /// remaining of the file.
-        length: Option<u64> = "length" => optional
+        length: Option<u64> = "length"
     ],
     children: [
         /// List of hashes for this range.
@@ -344,10 +344,10 @@ generate_element!(
     Received, "received", JINGLE_FT,
     attributes: [
         /// The content identifier of this Jingle session.
-        name: ContentId = "name" => required,
+        name: Required<ContentId> = "name",
 
         /// The creator of this file transfer.
-        creator: Creator = "creator" => required,
+        creator: Required<Creator> = "creator",
     ]
 );
 

src/jingle_ibb.rs πŸ”—

@@ -12,13 +12,13 @@ generate_element!(
 Transport, "transport", JINGLE_IBB,
 attributes: [
     /// Maximum size in bytes for each chunk.
-    block_size: u16 = "block-size" => required,
+    block_size: Required<u16> = "block-size",
 
     /// The identifier to be used to create a stream.
-    sid: StreamId = "sid" => required,
+    sid: Required<StreamId> = "sid",
 
     /// Which stanza type to use to exchange data.
-    stanza: Stanza = "stanza" => default,
+    stanza: Default<Stanza> = "stanza",
 ]);
 
 #[cfg(test)]

src/jingle_s5b.rs πŸ”—

@@ -55,23 +55,23 @@ generate_element!(
     Candidate, "candidate", JINGLE_S5B,
     attributes: [
         /// The identifier for this candidate.
-        cid: CandidateId = "cid" => required,
+        cid: Required<CandidateId> = "cid",
 
         /// The host to connect to.
-        host: IpAddr = "host" => required,
+        host: Required<IpAddr> = "host",
 
         /// The JID to request at the given end.
-        jid: Jid = "jid" => required,
+        jid: Required<Jid> = "jid",
 
         /// The port to connect to.
-        port: Option<u16> = "port" => optional,
+        port: Option<u16> = "port",
 
         /// The priority of this candidate, computed using this formula:
         /// priority = (2^16)*(type preference) + (local preference)
-        priority: u32 = "priority" => required,
+        priority: Required<u32> = "priority",
 
         /// The type of the connection being proposed by this candidate.
-        type_: Type = "type" => default,
+        type_: Default<Type> = "type",
     ]
 );
 

src/mam.rs πŸ”—

@@ -27,10 +27,10 @@ generate_element!(
     attributes: [
         /// An optional identifier for matching forwarded messages to this
         /// query.
-        queryid: Option<QueryId> = "queryid" => optional,
+        queryid: Option<QueryId> = "queryid",
 
         /// Must be set to Some when querying a PubSub node’s archive.
-        node: Option<NodeName> = "node" => optional
+        node: Option<NodeName> = "node"
     ],
     children: [
         /// Used for filtering the results.
@@ -50,11 +50,11 @@ generate_element!(
     Result_, "result", MAM,
     attributes: [
         /// The stanza-id under which the archive stored this stanza.
-        id: String = "id" => required,
+        id: Required<String> = "id",
 
         /// The same queryid as the one requested in the
         /// [query](struct.Query.html).
-        queryid: Option<QueryId> = "queryid" => optional,
+        queryid: Option<QueryId> = "queryid",
     ],
     children: [
         /// The actual stanza being forwarded.
@@ -76,7 +76,7 @@ generate_element!(
     Fin, "fin", MAM,
     attributes: [
         /// True when the end of a MAM query has been reached.
-        complete: Complete = "complete" => default
+        complete: Default<Complete> = "complete",
     ],
     children: [
         /// Describes the current page, it should contain at least [first]

src/media_element.rs πŸ”—

@@ -17,7 +17,7 @@ generate_element!(
         /// accepted too.
         ///
         /// [1]: https://www.iana.org/assignments/media-types/media-types.xhtml
-        type_: String = "type" => required
+        type_: Required<String> = "type"
     ],
     text: (
         /// The actual URI contained.
@@ -31,10 +31,10 @@ generate_element!(
     MediaElement, "media", MEDIA_ELEMENT,
     attributes: [
         /// The recommended display width in pixels.
-        width: Option<usize> = "width" => optional,
+        width: Option<usize> = "width",
 
         /// The recommended display height in pixels.
-        height: Option<usize> = "height" => optional
+        height: Option<usize> = "height"
     ],
     children: [
         /// A list of URIs referencing this media.

src/message_correct.rs πŸ”—

@@ -12,7 +12,7 @@ generate_element!(
     Replace, "replace", MESSAGE_CORRECT,
     attributes: [
         /// The 'id' attribute of the message getting corrected.
-        id: String = "id" => required,
+        id: Required<String> = "id",
     ]
 );
 

src/muc/muc.rs πŸ”—

@@ -14,16 +14,16 @@ generate_element!(
     History, "history", MUC,
     attributes: [
         /// How many characters of history to send, in XML characters.
-        maxchars: Option<u32> = "maxchars" => optional,
+        maxchars: Option<u32> = "maxchars",
 
         /// How many messages to send.
-        maxstanzas: Option<u32> = "maxstanzas" => optional,
+        maxstanzas: Option<u32> = "maxstanzas",
 
         /// Only send messages received in these last seconds.
-        seconds: Option<u32> = "seconds" => optional,
+        seconds: Option<u32> = "seconds",
 
         /// Only send messages after this date.
-        since: Option<DateTime> = "since" => optional,
+        since: Option<DateTime> = "since",
     ]
 );
 

src/muc/user.rs πŸ”—

@@ -128,7 +128,7 @@ generate_element!(
     Continue, "continue", MUC_USER,
     attributes: [
         /// The thread to continue in this room.
-        thread: Option<String> = "thread" => optional,
+        thread: Option<String> = "thread",
     ]
 );
 
@@ -187,16 +187,16 @@ generate_element!(
     /// An item representing a user in a room.
     Item, "item", MUC_USER, attributes: [
         /// The affiliation of this user with the room.
-        affiliation: Affiliation = "affiliation" => required,
+        affiliation: Required<Affiliation> = "affiliation",
 
         /// The real JID of this user, if you are allowed to see it.
-        jid: Option<Jid> = "jid" => optional,
+        jid: Option<Jid> = "jid",
 
         /// The current nickname of this user.
-        nick: Option<String> = "nick" => optional,
+        nick: Option<String> = "nick",
 
         /// The current role of this user.
-        role: Role = "role" => required
+        role: Required<Role> = "role",
     ], children: [
         /// The actor affected by this item.
         actor: Option<Actor> = ("actor", MUC_USER) => Actor,

src/pubsub/pubsub.rs πŸ”—

@@ -20,7 +20,7 @@ generate_element!(
     Affiliations, "affiliations", PUBSUB,
     attributes: [
         /// The optional node name this request pertains to.
-        node: Option<NodeName> = "node" => optional,
+        node: Option<NodeName> = "node",
     ],
     children: [
         /// The actual list of affiliation elements.
@@ -56,10 +56,10 @@ generate_element!(
     Affiliation, "affiliation", PUBSUB,
     attributes: [
         /// The node this affiliation pertains to.
-        node: NodeName = "node" => required,
+        node: Required<NodeName> = "node",
 
         /// The affiliation you currently have on this node.
-        affiliation: AffiliationAttribute = "affiliation" => required,
+        affiliation: Required<AffiliationAttribute> = "affiliation",
     ]
 );
 
@@ -77,7 +77,7 @@ generate_element!(
     Create, "create", PUBSUB,
     attributes: [
         /// The node name to create, if `None` the service will generate one.
-        node: Option<NodeName> = "node" => optional,
+        node: Option<NodeName> = "node",
     ]
 );
 
@@ -86,10 +86,10 @@ generate_element!(
     Default, "default", PUBSUB,
     attributes: [
         /// The node targetted by this request, otherwise the entire service.
-        node: Option<NodeName> = "node" => optional,
+        node: Option<NodeName> = "node",
 
         // TODO: do we really want to support collection nodes?
-        // type: String = "type" => optional,
+        // type: Option<String> = "type",
     ]
 );
 
@@ -99,13 +99,13 @@ generate_element!(
     attributes: [
         // TODO: should be an xs:positiveInteger, that is, an unbounded int β‰₯ 1.
         /// Maximum number of items returned.
-        max_items: Option<u32> = "max_items" => optional,
+        max_items: Option<u32> = "max_items",
 
         /// The node queried by this request.
-        node: NodeName = "node" => required,
+        node: Required<NodeName> = "node",
 
         /// The subscription identifier related to this request.
-        subid: Option<SubscriptionId> = "subid" => optional,
+        subid: Option<SubscriptionId> = "subid",
     ],
     children: [
         /// The actual list of items returned.
@@ -124,13 +124,13 @@ generate_element!(
     Options, "options", PUBSUB,
     attributes: [
         /// The JID affected by this request.
-        jid: Jid = "jid" => required,
+        jid: Required<Jid> = "jid",
 
         /// The node affected by this request.
-        node: Option<NodeName> = "node" => optional,
+        node: Option<NodeName> = "node",
 
         /// The subscription identifier affected by this request.
-        subid: Option<SubscriptionId> = "subid" => optional,
+        subid: Option<SubscriptionId> = "subid",
     ],
     children: [
         /// The form describing the subscription.
@@ -143,7 +143,7 @@ generate_element!(
     Publish, "publish", PUBSUB,
     attributes: [
         /// The target node for this operation.
-        node: NodeName = "node" => required,
+        node: Required<NodeName> = "node",
     ],
     children: [
         /// The items you want to publish.
@@ -172,10 +172,10 @@ generate_element!(
     Retract, "retract", PUBSUB,
     attributes: [
         /// The node affected by this request.
-        node: NodeName = "node" => required,
+        node: Required<NodeName> = "node",
 
         /// Whether a retract request should notify subscribers or not.
-        notify: Notify = "notify" => default,
+        notify: Default<Notify> = "notify",
     ],
     children: [
         /// The items affected by this request.
@@ -233,10 +233,10 @@ generate_element!(
     Subscribe, "subscribe", PUBSUB,
     attributes: [
         /// The JID being subscribed.
-        jid: Jid = "jid" => required,
+        jid: Required<Jid> = "jid",
 
         /// The node to subscribe to.
-        node: Option<NodeName> = "node" => optional,
+        node: Option<NodeName> = "node",
     ]
 );
 
@@ -245,7 +245,7 @@ generate_element!(
     Subscriptions, "subscriptions", PUBSUB,
     attributes: [
         /// The node to query.
-        node: Option<NodeName> = "node" => optional,
+        node: Option<NodeName> = "node",
     ],
     children: [
         /// The list of subscription elements returned.
@@ -258,16 +258,16 @@ generate_element!(
     SubscriptionElem, "subscription", PUBSUB,
     attributes: [
         /// The JID affected by this subscription.
-        jid: Jid = "jid" => required,
+        jid: Required<Jid> = "jid",
 
         /// The node affected by this subscription.
-        node: Option<NodeName> = "node" => optional,
+        node: Option<NodeName> = "node",
 
         /// The subscription identifier for this subscription.
-        subid: Option<SubscriptionId> = "subid" => optional,
+        subid: Option<SubscriptionId> = "subid",
 
         /// The state of the subscription.
-        subscription: Option<Subscription> = "subscription" => optional,
+        subscription: Option<Subscription> = "subscription",
     ],
     children: [
         /// The options related to this subscription.
@@ -280,13 +280,13 @@ generate_element!(
     Unsubscribe, "unsubscribe", PUBSUB,
     attributes: [
         /// The JID affected by this request.
-        jid: Jid = "jid" => required,
+        jid: Required<Jid> = "jid",
 
         /// The node affected by this request.
-        node: Option<NodeName> = "node" => optional,
+        node: Option<NodeName> = "node",
 
         /// The subscription identifier for this subscription.
-        subid: Option<SubscriptionId> = "subid" => optional,
+        subid: Option<SubscriptionId> = "subid",
     ]
 );
 

src/receipts.rs πŸ”—

@@ -22,7 +22,7 @@ generate_element!(
     Received, "received", RECEIPTS,
     attributes: [
         /// The 'id' attribute of the received message.
-        id: Option<String> = "id" => optional,
+        id: Option<String> = "id",
     ]
 );
 

src/roster.rs πŸ”—

@@ -50,16 +50,16 @@ generate_element!(
     Item, "item", ROSTER,
     attributes: [
         /// JID of this contact.
-        jid: Jid = "jid" => required,
+        jid: Required<Jid> = "jid",
 
         /// Name of this contact.
-        name: Option<String> = "name" => optional_empty,
+        name: OptionEmpty<String> = "name",
 
         /// Subscription status of this contact.
-        subscription: Subscription = "subscription" => default,
+        subscription: Default<Subscription> = "subscription",
 
         /// Indicates β€œPending Out” sub-states for this contact.
-        ask: Ask = "ask" => default,
+        ask: Default<Ask> = "ask",
     ],
 
     children: [
@@ -77,7 +77,7 @@ generate_element!(
         /// This is an opaque string that should only be sent back to the server on
         /// a new connection, if this client is storing the contact list between
         /// connections.
-        ver: Option<String> = "ver" => optional
+        ver: Option<String> = "ver"
     ],
     children: [
         /// List of the contacts of the user.

src/sasl.rs πŸ”—

@@ -49,7 +49,7 @@ generate_element!(
     Auth, "auth", SASL,
     attributes: [
         /// The mechanism used.
-        mechanism: Mechanism = "mechanism" => required
+        mechanism: Required<Mechanism> = "mechanism"
     ],
     text: (
         /// The content of the handshake.

src/sm.rs πŸ”—

@@ -11,7 +11,7 @@ generate_element!(
     A, "a", SM,
     attributes: [
         /// The last handled stanza.
-        h: u32 = "h" => required,
+        h: Required<u32> = "h",
     ]
 );
 
@@ -36,10 +36,10 @@ generate_element!(
     attributes: [
         /// The preferred resumption time in seconds by the client.
         // TODO: should be the infinite integer set β‰₯ 1.
-        max: Option<u32> = "max" => optional,
+        max: Option<u32> = "max",
 
         /// Whether the client wants to be allowed to resume the stream.
-        resume: ResumeAttr = "resume" => default,
+        resume: Default<ResumeAttr> = "resume",
     ]
 );
 
@@ -72,18 +72,18 @@ generate_element!(
     Enabled, "enabled", SM,
     attributes: [
         /// A random identifier used for stream resumption.
-        id: Option<StreamId> = "id" => optional,
+        id: Option<StreamId> = "id",
 
         /// The preferred IP, domain, IP:port or domain:port location for
         /// resumption.
-        location: Option<String> = "location" => optional,
+        location: Option<String> = "location",
 
         /// The preferred resumption time in seconds by the server.
         // TODO: should be the infinite integer set β‰₯ 1.
-        max: Option<u32> = "max" => optional,
+        max: Option<u32> = "max",
 
         /// Whether stream resumption is allowed.
-        resume: ResumeAttr = "resume" => default,
+        resume: Default<ResumeAttr> = "resume",
     ]
 );
 
@@ -92,7 +92,7 @@ generate_element!(
     Failed, "failed", SM,
     attributes: [
         /// The last handled stanza.
-        h: Option<u32> = "h" => optional,
+        h: Option<u32> = "h",
     ],
     children: [
         /// The error returned.
@@ -113,11 +113,11 @@ generate_element!(
     Resume, "resume", SM,
     attributes: [
         /// The last handled stanza.
-        h: u32 = "h" => required,
+        h: Required<u32> = "h",
 
         /// The previous id given by the server on
         /// [enabled](struct.Enabled.html).
-        previd: StreamId = "previd" => required,
+        previd: Required<StreamId> = "previd",
     ]
 );
 
@@ -126,11 +126,11 @@ generate_element!(
     Resumed, "resumed", SM,
     attributes: [
         /// The last handled stanza.
-        h: u32 = "h" => required,
+        h: Required<u32> = "h",
 
         /// The previous id given by the server on
         /// [enabled](struct.Enabled.html).
-        previd: StreamId = "previd" => required,
+        previd: Required<StreamId> = "previd",
     ]
 );
 

src/stanza_id.rs πŸ”—

@@ -13,10 +13,10 @@ generate_element!(
     StanzaId, "stanza-id", SID,
     attributes: [
         /// The id associated to this stanza by another entity.
-        id: String = "id" => required,
+        id: Required<String> = "id",
 
         /// The entity who stamped this stanza-id.
-        by: Jid = "by" => required,
+        by: Required<Jid> = "by",
     ]
 );
 
@@ -28,7 +28,7 @@ generate_element!(
     OriginId, "origin-id", SID,
     attributes: [
         /// The id this client set for this stanza.
-        id: String = "id" => required,
+        id: Required<String> = "id",
     ]
 );
 

src/stream.rs πŸ”—

@@ -11,20 +11,20 @@ generate_element!(
     Stream, "stream", STREAM,
     attributes: [
         /// The JID of the entity opening this stream.
-        from: Option<Jid> = "from" => optional,
+        from: Option<Jid> = "from",
 
         /// The JID of the entity receiving this stream opening.
-        to: Option<Jid> = "to" => optional,
+        to: Option<Jid> = "to",
 
         /// The id of the stream, used for authentication challenges.
-        id: Option<String> = "id" => optional,
+        id: Option<String> = "id",
 
         /// The XMPP version used during this stream.
-        version: Option<String> = "version" => optional,
+        version: Option<String> = "version",
 
         /// The default human language for all subsequent stanzas, which will
         /// be transmitted to other entities for better localisation.
-        xml_lang: Option<String> = "xml:lang" => optional,
+        xml_lang: Option<String> = "xml:lang",
     ]
 );
 

src/util/macros.rs πŸ”—

@@ -8,20 +8,20 @@ macro_rules! get_attr {
     ($elem:ident, $attr:tt, $type:tt) => {
         get_attr!($elem, $attr, $type, value, value.parse()?)
     };
-    ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => {
+    ($elem:ident, $attr:tt, OptionEmpty, $value:ident, $func:expr) => {
         match $elem.attr($attr) {
             Some("") => None,
             Some($value) => Some($func),
             None => None,
         }
     };
-    ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => {
+    ($elem:ident, $attr:tt, Option, $value:ident, $func:expr) => {
         match $elem.attr($attr) {
             Some($value) => Some($func),
             None => None,
         }
     };
-    ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => {
+    ($elem:ident, $attr:tt, Required, $value:ident, $func:expr) => {
         match $elem.attr($attr) {
             Some($value) => $func,
             None => {
@@ -33,12 +33,22 @@ macro_rules! get_attr {
             }
         }
     };
-    ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => {
+    ($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => {
         match $elem.attr($attr) {
             Some($value) => $func,
             None => ::std::default::Default::default(),
         }
     };
+    // The remaining ones are only for backwards-compatibility.
+    ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => {
+        get_attr!($elem, $attr, Option, $value, $func)
+    };
+    ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => {
+        get_attr!($elem, $attr, Required, $value, $func)
+    };
+    ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => {
+        get_attr!($elem, $attr, Default, $value, $func)
+    };
 }
 
 macro_rules! generate_attribute {
@@ -398,6 +408,21 @@ macro_rules! generate_elem_id {
     );
 }
 
+macro_rules! decl_attr {
+    (OptionEmpty, $type:ty) => (
+        Option<$type>
+    );
+    (Option, $type:ty) => (
+        Option<$type>
+    );
+    (Required, $type:ty) => (
+        $type
+    );
+    (Default, $type:ty) => (
+        $type
+    );
+}
+
 macro_rules! start_decl {
     (Vec, $type:ty) => (
         Vec<$type>
@@ -503,31 +528,31 @@ macro_rules! generate_serialiser {
 }
 
 macro_rules! generate_element {
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => (
-        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []);
+    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => (
+        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => (
-        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []);
+    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => (
+        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
     );
     ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => (
         generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => (
-        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
+    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => (
+        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
     );
     ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
         generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
-        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
+    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
+        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => (
+    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => (
         $(#[$meta])*
         #[derive(Debug, Clone)]
         pub struct $elem {
             $(
                 $(#[$attr_meta])*
-                pub $attr: $attr_type,
+                pub $attr: decl_attr!($attr_action, $attr_type),
             )*
             $(
                 $(#[$child_meta])*

src/websocket.rs πŸ”—

@@ -11,20 +11,20 @@ generate_element!(
     Open, "open", WEBSOCKET,
     attributes: [
         /// The JID of the entity opening this stream.
-        from: Option<Jid> = "from" => optional,
+        from: Option<Jid> = "from",
 
         /// The JID of the entity receiving this stream opening.
-        to: Option<Jid> = "to" => optional,
+        to: Option<Jid> = "to",
 
         /// The id of the stream, used for authentication challenges.
-        id: Option<String> = "id" => optional,
+        id: Option<String> = "id",
 
         /// The XMPP version used during this stream.
-        version: Option<String> = "version" => optional,
+        version: Option<String> = "version",
 
         /// The default human language for all subsequent stanzas, which will
         /// be transmitted to other entities for better localisation.
-        xml_lang: Option<String> = "xml:lang" => optional,
+        xml_lang: Option<String> = "xml:lang",
     ]
 );