Add a generate_element_with_only_attributes macro, and use it wherever it makes sense.

This commit is contained in:
Emmanuel Gil Peyrot 2017-10-31 17:58:11 +00:00
commit 929bd577f3
10 changed files with 92 additions and 365 deletions

View file

@ -13,39 +13,14 @@ use error::Error;
use ns; use ns;
/// Structure representing an `<encryption xmlns='urn:xmpp:eme:0'/>` element. /// Structure representing an `<encryption xmlns='urn:xmpp:eme:0'/>` element.
#[derive(Debug, Clone)] generate_element_with_only_attributes!(ExplicitMessageEncryption, "encryption", ns::EME, [
pub struct ExplicitMessageEncryption { // Namespace of the encryption scheme used.
/// Namespace of the encryption scheme used. namespace: String = "namespace" => required,
pub namespace: String,
/// User-friendly name for the encryption scheme, should be `None` for OTR, // User-friendly name for the encryption scheme, should be `None` for OTR,
/// legacy OpenPGP and OX. // legacy OpenPGP and OX.
pub name: Option<String>, name: Option<String> = "name" => optional,
} ]);
impl TryFrom<Element> for ExplicitMessageEncryption {
type Err = Error;
fn try_from(elem: Element) -> Result<ExplicitMessageEncryption, Error> {
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<ExplicitMessageEncryption> for Element {
fn from(eme: ExplicitMessageEncryption) -> Element {
Element::builder("encryption")
.ns(ns::EME)
.attr("namespace", eme.namespace)
.attr("name", eme.name)
.build()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -19,41 +19,11 @@ generate_attribute!(Stanza, "stanza", {
Message => "message", Message => "message",
}, Default = Iq); }, Default = Iq);
#[derive(Debug, Clone)] generate_element_with_only_attributes!(Open, "open", ns::IBB, [
pub struct Open { block_size: u16 = "block-size" => required,
pub block_size: u16, sid: String = "sid" => required,
pub sid: String, stanza: Stanza = "stanza" => default,
pub stanza: Stanza, ]);
}
impl TryFrom<Element> for Open {
type Err = Error;
fn try_from(elem: Element) -> Result<Open, Error> {
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<Open> 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()
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Data { pub struct Data {
@ -91,35 +61,9 @@ impl From<Data> for Element {
} }
} }
#[derive(Debug, Clone)] generate_element_with_only_attributes!(Close, "close", ns::IBB, [
pub struct Close { sid: String = "sid" => required,
pub sid: String, ]);
}
impl TryFrom<Element> for Close {
type Err = Error;
fn try_from(elem: Element) -> Result<Close, Error> {
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<Close> for Element {
fn from(close: Close) -> Element {
Element::builder("close")
.ns(ns::IBB)
.attr("sid", close.sid)
.build()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -256,35 +256,10 @@ impl From<Checksum> for Element {
} }
} }
#[derive(Debug, Clone)] generate_element_with_only_attributes!(Received, "received", ns::JINGLE_FT, [
pub struct Received { name: ContentId = "name" => required,
pub name: ContentId, creator: Creator = "creator" => required,
pub creator: Creator, ]);
}
impl TryFrom<Element> for Received {
type Err = Error;
fn try_from(elem: Element) -> Result<Received, Error> {
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<Received> for Element {
fn from(received: Received) -> Element {
Element::builder("received")
.ns(ns::JINGLE_FT)
.attr("name", received.name)
.attr("creator", received.creator)
.build()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -17,41 +17,11 @@ use ibb::Stanza;
generate_id!(StreamId); generate_id!(StreamId);
#[derive(Debug, Clone)] generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [
pub struct Transport { block_size: u16 = "block-size" => required,
pub block_size: u16, sid: StreamId = "sid" => required,
pub sid: StreamId, stanza: Stanza = "stanza" => default,
pub stanza: Stanza, ]);
}
impl TryFrom<Element> for Transport {
type Err = Error;
fn try_from(elem: Element) -> Result<Transport, Error> {
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<Transport> 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()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -30,29 +30,14 @@ generate_id!(CandidateId);
generate_id!(StreamId); generate_id!(StreamId);
#[derive(Debug, Clone)] generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [
pub struct Candidate { cid: CandidateId = "cid" => required,
pub cid: CandidateId, host: String = "host" => required,
pub host: String, jid: Jid = "jid" => required,
pub jid: Jid, port: Option<u16> = "port" => optional,
pub port: Option<u16>, priority: u32 = "priority" => required,
pub priority: u32, type_: Type = "type" => default,
pub type_: Type, ]);
}
impl From<Candidate> 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()
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum TransportPayload { pub enum TransportPayload {

View file

@ -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<Element> 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 { macro_rules! generate_id {
($elem:ident) => ( ($elem:ident) => (
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

@ -12,31 +12,9 @@ use error::Error;
use ns; use ns;
#[derive(Debug, Clone)] generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [
pub struct Replace { id: String = "id" => required,
pub id: String, ]);
}
impl TryFrom<Element> for Replace {
type Err = Error;
fn try_from(elem: Element) -> Result<Replace, Error> {
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<Replace> for Element {
fn from(replace: Replace) -> Element {
Element::builder("replace")
.ns(ns::MESSAGE_CORRECT)
.attr("id", replace.id)
.build()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -192,38 +192,9 @@ impl From<Actor> for Element {
} }
} }
#[derive(Debug, Clone, PartialEq)] generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [
pub struct Continue { thread: Option<String> = "thread" => optional,
thread: Option<String>, ]);
}
impl TryFrom<Element> for Continue {
type Err = Error;
fn try_from(elem: Element) -> Result<Continue, Error> {
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<Continue> for Element {
fn from(cont: Continue) -> Element {
Element::builder("continue")
.ns(ns::MUC_USER)
.attr("thread", cont.thread)
.build()
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Reason(String); pub struct Reason(String);
@ -576,7 +547,7 @@ mod tests {
thread='foo'/> thread='foo'/>
".parse().unwrap(); ".parse().unwrap();
let continue_ = Continue::try_from(elem).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] #[test]
@ -725,7 +696,7 @@ mod tests {
let item = Item::try_from(elem).unwrap(); let item = Item::try_from(elem).unwrap();
let continue_1 = Continue { thread: Some("foobar".to_owned()) }; let continue_1 = Continue { thread: Some("foobar".to_owned()) };
match item { 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!(), _ => panic!(),
} }
} }

View file

@ -12,68 +12,11 @@ use error::Error;
use ns; use ns;
#[derive(Debug, Clone)] generate_empty_element!(Request, "request", ns::RECEIPTS);
pub struct Request;
impl TryFrom<Element> for Request { generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [
type Err = Error; id: Option<String> = "id" => optional,
]);
fn try_from(elem: Element) -> Result<Request, Error> {
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<Request> for Element {
fn from(_: Request) -> Element {
Element::builder("request")
.ns(ns::RECEIPTS)
.build()
}
}
#[derive(Debug, Clone)]
pub struct Received {
pub id: Option<String>,
}
impl TryFrom<Element> for Received {
type Err = Error;
fn try_from(elem: Element) -> Result<Received, Error> {
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<Received> for Element {
fn from(received: Received) -> Element {
Element::builder("received")
.ns(ns::RECEIPTS)
.attr("id", received.id)
.build()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -13,68 +13,14 @@ use error::Error;
use ns; use ns;
#[derive(Debug, Clone)] generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [
pub struct StanzaId { id: String = "id" => required,
pub id: String, by: Jid = "by" => required,
pub by: Jid, ]);
}
impl TryFrom<Element> for StanzaId { generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [
type Err = Error; id: String = "id" => required,
]);
fn try_from(elem: Element) -> Result<StanzaId, Error> {
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<StanzaId> 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<Element> for OriginId {
type Err = Error;
fn try_from(elem: Element) -> Result<OriginId, Error> {
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<OriginId> for Element {
fn from(origin_id: OriginId) -> Element {
Element::builder("origin-id")
.ns(ns::SID)
.attr("id", origin_id.id)
.build()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {