From 929bd577f328bf5fa3866ff56195732038e6b626 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:58:11 +0000 Subject: [PATCH] Add a generate_element_with_only_attributes macro, and use it wherever it makes sense. --- src/eme.rs | 39 ++++------------------- src/ibb.rs | 72 +++++------------------------------------- src/jingle_ft.rs | 33 +++---------------- src/jingle_ibb.rs | 40 +++-------------------- src/jingle_s5b.rs | 31 +++++------------- src/lib.rs | 40 +++++++++++++++++++++++ src/message_correct.rs | 28 ++-------------- src/muc/user.rs | 39 +++-------------------- src/receipts.rs | 65 +++----------------------------------- src/stanza_id.rs | 70 +++++----------------------------------- 10 files changed, 92 insertions(+), 365 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index ad4b437f8098a5cc18551277f7b03ce4eebf4210..540b50730df1036c450f80478b44fd77fa1738ca 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -13,39 +13,14 @@ use error::Error; use ns; /// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct ExplicitMessageEncryption { - /// Namespace of the encryption scheme used. - pub namespace: String, +generate_element_with_only_attributes!(ExplicitMessageEncryption, "encryption", ns::EME, [ + // Namespace of the encryption scheme used. + namespace: String = "namespace" => required, - /// User-friendly name for the encryption scheme, should be `None` for OTR, - /// legacy OpenPGP and OX. - pub name: Option, -} - -impl TryFrom for ExplicitMessageEncryption { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "encryption", ns::EME); - check_no_children!(elem, "encryption"); - check_no_unknown_attributes!(elem, "encryption", ["namespace", "name"]); - Ok(ExplicitMessageEncryption { - namespace: get_attr!(elem, "namespace", required), - name: get_attr!(elem, "name", optional), - }) - } -} - -impl From for Element { - fn from(eme: ExplicitMessageEncryption) -> Element { - Element::builder("encryption") - .ns(ns::EME) - .attr("namespace", eme.namespace) - .attr("name", eme.name) - .build() - } -} + // User-friendly name for the encryption scheme, should be `None` for OTR, + // legacy OpenPGP and OX. + name: Option = "name" => optional, +]); #[cfg(test)] mod tests { diff --git a/src/ibb.rs b/src/ibb.rs index 2c01e1c741aa9785c5a308471f5bbfea2ed9e9d5..7d7f3e73fa501e4a7ffd63e1586bac52ba8532ee 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -19,41 +19,11 @@ generate_attribute!(Stanza, "stanza", { Message => "message", }, Default = Iq); -#[derive(Debug, Clone)] -pub struct Open { - pub block_size: u16, - pub sid: String, - pub stanza: Stanza, -} - -impl TryFrom for Open { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("open", ns::IBB) { - return Err(Error::ParseError("This is not an open element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in open element.")); - } - Ok(Open { - block_size: get_attr!(elem, "block-size", required), - sid: get_attr!(elem, "sid", required), - stanza: get_attr!(elem, "stanza", default), - }) - } -} - -impl From for Element { - fn from(open: Open) -> Element { - Element::builder("open") - .ns(ns::IBB) - .attr("block-size", open.block_size) - .attr("sid", open.sid) - .attr("stanza", open.stanza) - .build() - } -} +generate_element_with_only_attributes!(Open, "open", ns::IBB, [ + block_size: u16 = "block-size" => required, + sid: String = "sid" => required, + stanza: Stanza = "stanza" => default, +]); #[derive(Debug, Clone)] pub struct Data { @@ -91,35 +61,9 @@ impl From for Element { } } -#[derive(Debug, Clone)] -pub struct Close { - pub sid: String, -} - -impl TryFrom for Close { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("close", ns::IBB) { - return Err(Error::ParseError("This is not a close element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in close element.")); - } - Ok(Close { - sid: get_attr!(elem, "sid", required), - }) - } -} - -impl From for Element { - fn from(close: Close) -> Element { - Element::builder("close") - .ns(ns::IBB) - .attr("sid", close.sid) - .build() - } -} +generate_element_with_only_attributes!(Close, "close", ns::IBB, [ + sid: String = "sid" => required, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 7e13351f8a96de92f578241d0197d8e624aeaa95..15a21c67efa16c70df390cb3774b4d3d505a59de 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -256,35 +256,10 @@ impl From for Element { } } -#[derive(Debug, Clone)] -pub struct Received { - pub name: ContentId, - pub creator: Creator, -} - -impl TryFrom for Received { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "received", ns::JINGLE_FT); - check_no_children!(elem, "received"); - check_no_unknown_attributes!(elem, "received", ["name", "creator"]); - Ok(Received { - name: get_attr!(elem, "name", required), - creator: get_attr!(elem, "creator", required), - }) - } -} - -impl From for Element { - fn from(received: Received) -> Element { - Element::builder("received") - .ns(ns::JINGLE_FT) - .attr("name", received.name) - .attr("creator", received.creator) - .build() - } -} +generate_element_with_only_attributes!(Received, "received", ns::JINGLE_FT, [ + name: ContentId = "name" => required, + creator: Creator = "creator" => required, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index ed1c6d189ec5c5662aa4aab3682bd58cde9223a9..632540ed2cc909d22c3625a5e621a1c6565ab498 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -17,41 +17,11 @@ use ibb::Stanza; generate_id!(StreamId); -#[derive(Debug, Clone)] -pub struct Transport { - pub block_size: u16, - pub sid: StreamId, - pub stanza: Stanza, -} - -impl TryFrom for Transport { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("transport", ns::JINGLE_IBB) { - return Err(Error::ParseError("This is not an JingleIBB element.")) - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in JingleIBB element.")); - } - Ok(Transport { - block_size: get_attr!(elem, "block-size", required), - sid: get_attr!(elem, "sid", required), - stanza: get_attr!(elem, "stanza", default), - }) - } -} - -impl From for Element { - fn from(transport: Transport) -> Element { - Element::builder("transport") - .ns(ns::JINGLE_IBB) - .attr("block-size", transport.block_size) - .attr("sid", transport.sid) - .attr("stanza", transport.stanza) - .build() - } -} +generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ + block_size: u16 = "block-size" => required, + sid: StreamId = "sid" => required, + stanza: Stanza = "stanza" => default, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 6f8bb902106ebcdbdd1dedae5f00fda9c57d53da..f1396c5df4d1d1c1fdf032fa18c324a17cba229e 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -30,29 +30,14 @@ generate_id!(CandidateId); generate_id!(StreamId); -#[derive(Debug, Clone)] -pub struct Candidate { - pub cid: CandidateId, - pub host: String, - pub jid: Jid, - pub port: Option, - pub priority: u32, - pub type_: Type, -} - -impl From for Element { - fn from(candidate: Candidate) -> Element { - Element::builder("candidate") - .ns(ns::JINGLE_S5B) - .attr("cid", candidate.cid) - .attr("host", candidate.host) - .attr("jid", String::from(candidate.jid)) - .attr("port", candidate.port) - .attr("priority", candidate.priority) - .attr("type", candidate.type_) - .build() - } -} +generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ + cid: CandidateId = "cid" => required, + host: String = "host" => required, + jid: Jid = "jid" => required, + port: Option = "port" => optional, + priority: u32 = "priority" => required, + type_: Type = "type" => default, +]); #[derive(Debug, Clone)] pub enum TransportPayload { diff --git a/src/lib.rs b/src/lib.rs index ccc84f022575287748020885e952882d986750be..5e767fc20acc13ebb29e75c3cc458985324065e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -203,6 +203,46 @@ macro_rules! generate_empty_element { ); } +macro_rules! generate_element_with_only_attributes { + ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_only_attributes!($elem, $name, $ns, [$($attr: $attr_type = $attr_name => $attr_action),*]); + ); + ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + #[derive(Debug, Clone)] + pub struct $elem { + $( + pub $attr: $attr_type + ),* + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),* + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .build() + } + } + ); +} + macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/message_correct.rs b/src/message_correct.rs index d8200ae19b07401e21a548a6e0b0018e7e4efa1e..26cdcf99596b8c79d8403dabbe108f46e8800d02 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -12,31 +12,9 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Replace { - pub id: String, -} - -impl TryFrom for Replace { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "replace", ns::MESSAGE_CORRECT); - check_no_children!(elem, "replace"); - check_no_unknown_attributes!(elem, "replace", ["id"]); - let id = get_attr!(elem, "id", required); - Ok(Replace { id }) - } -} - -impl From for Element { - fn from(replace: Replace) -> Element { - Element::builder("replace") - .ns(ns::MESSAGE_CORRECT) - .attr("id", replace.id) - .build() - } -} +generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [ + id: String = "id" => required, +]); #[cfg(test)] mod tests { diff --git a/src/muc/user.rs b/src/muc/user.rs index 322525cbe2a3cadbe0a292f23f401947d8c15b3f..fb30967993d0d417959a373a168f9a98232590fd 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -192,38 +192,9 @@ impl From for Element { } } -#[derive(Debug, Clone, PartialEq)] -pub struct Continue { - thread: Option, -} - -impl TryFrom for Continue { - type Err = 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 From for Element { - fn from(cont: Continue) -> Element { - Element::builder("continue") - .ns(ns::MUC_USER) - .attr("thread", cont.thread) - .build() - } -} +generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [ + thread: Option = "thread" => optional, +]); #[derive(Debug, Clone, PartialEq)] pub struct Reason(String); @@ -576,7 +547,7 @@ mod tests { thread='foo'/> ".parse().unwrap(); let continue_ = Continue::try_from(elem).unwrap(); - assert_eq!(continue_, Continue { thread: Some("foo".to_owned()) }); + assert_eq!(continue_.thread, Some("foo".to_owned())); } #[test] @@ -725,7 +696,7 @@ mod tests { 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), + Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2.thread, continue_1.thread), _ => panic!(), } } diff --git a/src/receipts.rs b/src/receipts.rs index 6ccec7eae1f500062175bfdc776eb5e3ac178106..470a46474dde9241e8fc74570fa186429aa0ace9 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -12,68 +12,11 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Request; +generate_empty_element!(Request, "request", ns::RECEIPTS); -impl TryFrom for Request { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("request", ns::RECEIPTS) { - return Err(Error::ParseError("This is not a request element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in request element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in request element.")); - } - Ok(Request) - } -} - -impl From for Element { - fn from(_: Request) -> Element { - Element::builder("request") - .ns(ns::RECEIPTS) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct Received { - pub id: Option, -} - -impl TryFrom for Received { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("received", ns::RECEIPTS) { - return Err(Error::ParseError("This is not a received element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in received element.")); - } - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in received element.")); - } - } - Ok(Received { - id: get_attr!(elem, "id", optional), - }) - } -} - -impl From for Element { - fn from(received: Received) -> Element { - Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", received.id) - .build() - } -} +generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ + id: Option = "id" => optional, +]); #[cfg(test)] mod tests { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index feeeadbe0328d00411e776d557227ed9dc7439c4..6e65e012b885d13e0b1458b3687f710743a21746 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -13,68 +13,14 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct StanzaId { - pub id: String, - pub by: Jid, -} - -impl TryFrom for StanzaId { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("stanza-id", ns::SID) { - return Err(Error::ParseError("This is not a stanza-id element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in stanza-id element.")); - } - Ok(StanzaId { - id: get_attr!(elem, "id", required), - by: get_attr!(elem, "by", required), - }) - } -} - -impl From for Element { - fn from(stanza_id: StanzaId) -> Element { - Element::builder("stanza-id") - .ns(ns::SID) - .attr("id", stanza_id.id) - .attr("by", stanza_id.by) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct OriginId { - pub id: String, -} - -impl TryFrom for OriginId { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("origin-id", ns::SID) { - return Err(Error::ParseError("This is not an origin-id element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in origin-id element.")); - } - Ok(OriginId { - id: get_attr!(elem, "id", required), - }) - } -} - -impl From for Element { - fn from(origin_id: OriginId) -> Element { - Element::builder("origin-id") - .ns(ns::SID) - .attr("id", origin_id.id) - .build() - } -} +generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [ + id: String = "id" => required, + by: Jid = "by" => required, +]); + +generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ + id: String = "id" => required, +]); #[cfg(test)] mod tests {