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;
/// Structure representing an `<encryption xmlns='urn:xmpp:eme:0'/>` 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<String>,
}
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()
}
}
// User-friendly name for the encryption scheme, should be `None` for OTR,
// legacy OpenPGP and OX.
name: Option<String> = "name" => optional,
]);
#[cfg(test)]
mod tests {

View file

@ -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<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()
}
}
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<Data> for Element {
}
}
#[derive(Debug, Clone)]
pub struct Close {
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()
}
}
generate_element_with_only_attributes!(Close, "close", ns::IBB, [
sid: String = "sid" => required,
]);
#[cfg(test)]
mod tests {

View file

@ -256,35 +256,10 @@ impl From<Checksum> for Element {
}
}
#[derive(Debug, Clone)]
pub struct Received {
pub name: ContentId,
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()
}
}
generate_element_with_only_attributes!(Received, "received", ns::JINGLE_FT, [
name: ContentId = "name" => required,
creator: Creator = "creator" => required,
]);
#[cfg(test)]
mod tests {

View file

@ -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<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()
}
}
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 {

View file

@ -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<u16>,
pub priority: u32,
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()
}
}
generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [
cid: CandidateId = "cid" => required,
host: String = "host" => required,
jid: Jid = "jid" => required,
port: Option<u16> = "port" => optional,
priority: u32 = "priority" => required,
type_: Type = "type" => default,
]);
#[derive(Debug, Clone)]
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 {
($elem:ident) => (
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

@ -12,31 +12,9 @@ use error::Error;
use ns;
#[derive(Debug, Clone)]
pub struct Replace {
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()
}
}
generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [
id: String = "id" => required,
]);
#[cfg(test)]
mod tests {

View file

@ -192,38 +192,9 @@ impl From<Actor> for Element {
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Continue {
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()
}
}
generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [
thread: Option<String> = "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!(),
}
}

View file

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

View file

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