diff --git a/src/iq.rs b/src/iq.rs index c7728ae2cc05e479f3c59d9fff6288fdf87302d1..8a4e70e7d69e79882795e2ff3ad20b9be047b710 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,6 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use minidom::IntoAttributeValue; @@ -17,7 +19,7 @@ use ns; use stanza_error; use disco; use ibb; -use jingle; +use jingle::Jingle; use ping; /// Lists every known payload of a ``. @@ -25,7 +27,7 @@ use ping; pub enum IqPayload { Disco(disco::Disco), IBB(ibb::IBB), - Jingle(jingle::Jingle), + Jingle(Jingle), Ping(ping::Ping), } @@ -97,7 +99,7 @@ pub fn parse_iq(root: &Element) -> Result { Some(IqPayload::Disco(disco)) } else if let Ok(ibb) = ibb::parse_ibb(elem) { Some(IqPayload::IBB(ibb)) - } else if let Ok(jingle) = jingle::parse_jingle(elem) { + } else if let Ok(jingle) = Jingle::try_from(elem) { Some(IqPayload::Jingle(jingle)) } else if let Ok(ping) = ping::parse_ping(elem) { Some(IqPayload::Ping(ping)) @@ -152,7 +154,7 @@ pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), IqPayload::IBB(ref ibb) => ibb::serialise(ibb), - IqPayload::Jingle(ref jingle) => jingle::serialise(jingle), + IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(_) => ping::serialise_ping(), } } diff --git a/src/jingle.rs b/src/jingle.rs index 717a3b77217ebdc8b6f290c60e7df9ce348e86ce..dfe0d71046138649fb33f97e499a17319ef771f5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,10 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::Element; use error::Error; use ns; @@ -204,9 +204,9 @@ impl FromStr for Reason { } } -impl IntoElements for Reason { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = Element::builder(match self { +impl<'a> Into for &'a Reason { + fn into(self) -> Element { + Element::builder(match *self { Reason::AlternativeSession => "alternative-session", Reason::Busy => "busy", Reason::Cancel => "cancel", @@ -224,8 +224,7 @@ impl IntoElements for Reason { Reason::Timeout => "timeout", Reason::UnsupportedApplications => "unsupported-applications", Reason::UnsupportedTransports => "unsupported-transports", - }).build(); - emitter.append_child(elem); + }).build() } } @@ -246,195 +245,208 @@ pub struct Jingle { pub other: Vec, } -pub fn parse_jingle(root: &Element) -> Result { - if !root.is("jingle", ns::JINGLE) { - return Err(Error::ParseError("This is not a Jingle element.")); - } +impl<'a> TryFrom<&'a Element> for Jingle { + type Error = Error; - let mut contents: Vec = vec!(); - - let action = root.attr("action") - .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? - .parse()?; - let initiator = root.attr("initiator") - .and_then(|initiator| initiator.parse().ok()); - let responder = root.attr("responder") - .and_then(|responder| responder.parse().ok()); - let sid = root.attr("sid") - .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; - let mut reason_element = None; - let mut other = vec!(); - - for child in root.children() { - if child.is("content", ns::JINGLE) { - let creator = child.attr("creator") - .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? - .parse()?; - let disposition = child.attr("disposition") - .unwrap_or("session"); - let name = child.attr("name") - .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; - let senders = child.attr("senders") - .unwrap_or("both") - .parse()?; - let mut description = None; - let mut transport = None; - let mut security = None; - for stuff in child.children() { - if stuff.name() == "description" { - if description.is_some() { - return Err(Error::ParseError("Content must not have more than one description.")); - } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on description element."))?; - description = Some(( - namespace, - stuff.clone(), - )); - } else if stuff.name() == "transport" { - if transport.is_some() { - return Err(Error::ParseError("Content must not have more than one transport.")); - } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on transport element."))?; - transport = Some(( - namespace, - stuff.clone(), - )); - } else if stuff.name() == "security" { - if security.is_some() { - return Err(Error::ParseError("Content must not have more than one security.")); + fn try_from(root: &'a Element) -> Result { + if !root.is("jingle", ns::JINGLE) { + return Err(Error::ParseError("This is not a Jingle element.")); + } + + let mut contents: Vec = vec!(); + + let action = root.attr("action") + .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? + .parse()?; + let initiator = root.attr("initiator") + .and_then(|initiator| initiator.parse().ok()); + let responder = root.attr("responder") + .and_then(|responder| responder.parse().ok()); + let sid = root.attr("sid") + .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; + let mut reason_element = None; + let mut other = vec!(); + + for child in root.children() { + if child.is("content", ns::JINGLE) { + let creator = child.attr("creator") + .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? + .parse()?; + let disposition = child.attr("disposition") + .unwrap_or("session"); + let name = child.attr("name") + .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; + let senders = child.attr("senders") + .unwrap_or("both") + .parse()?; + let mut description = None; + let mut transport = None; + let mut security = None; + for stuff in child.children() { + if stuff.name() == "description" { + if description.is_some() { + return Err(Error::ParseError("Content must not have more than one description.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on description element."))?; + description = Some(( + namespace, + stuff.clone(), + )); + } else if stuff.name() == "transport" { + if transport.is_some() { + return Err(Error::ParseError("Content must not have more than one transport.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on transport element."))?; + transport = Some(( + namespace, + stuff.clone(), + )); + } else if stuff.name() == "security" { + if security.is_some() { + return Err(Error::ParseError("Content must not have more than one security.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on security element."))?; + security = Some(( + namespace, + stuff.clone(), + )); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on security element."))?; - security = Some(( - namespace, - stuff.clone(), - )); } - } - if description.is_none() { - return Err(Error::ParseError("Content must have one description.")); - } - if transport.is_none() { - return Err(Error::ParseError("Content must have one transport.")); - } - let description = description.unwrap().to_owned(); - let transport = transport.unwrap().to_owned(); - contents.push(Content { - creator: creator, - disposition: disposition.to_owned(), - name: name.to_owned(), - senders: senders, - description: description, - transport: transport, - security: security, - }); - } else if child.is("reason", ns::JINGLE) { - if reason_element.is_some() { - return Err(Error::ParseError("Jingle must not have more than one reason.")); - } - let mut reason = None; - let mut text = None; - for stuff in child.children() { - if stuff.ns() != Some(ns::JINGLE) { - return Err(Error::ParseError("Reason contains a foreign element.")); + if description.is_none() { + return Err(Error::ParseError("Content must have one description.")); + } + if transport.is_none() { + return Err(Error::ParseError("Content must have one transport.")); + } + let description = description.unwrap().to_owned(); + let transport = transport.unwrap().to_owned(); + contents.push(Content { + creator: creator, + disposition: disposition.to_owned(), + name: name.to_owned(), + senders: senders, + description: description, + transport: transport, + security: security, + }); + } else if child.is("reason", ns::JINGLE) { + if reason_element.is_some() { + return Err(Error::ParseError("Jingle must not have more than one reason.")); } - let name = stuff.name(); - if name == "text" { - if text.is_some() { - return Err(Error::ParseError("Reason must not have more than one text.")); + let mut reason = None; + let mut text = None; + for stuff in child.children() { + if stuff.ns() != Some(ns::JINGLE) { + return Err(Error::ParseError("Reason contains a foreign element.")); + } + let name = stuff.name(); + if name == "text" { + if text.is_some() { + return Err(Error::ParseError("Reason must not have more than one text.")); + } + text = Some(stuff.text()); + } else { + reason = Some(name.parse()?); } - text = Some(stuff.text()); - } else { - reason = Some(name.parse()?); } + if reason.is_none() { + return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); + } + reason_element = Some(ReasonElement { + reason: reason.unwrap(), + text: text, + }); + } else { + other.push(child.clone()); } - if reason.is_none() { - return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); - } - reason_element = Some(ReasonElement { - reason: reason.unwrap(), - text: text, - }); - } else { - other.push(child.clone()); } - } - Ok(Jingle { - action: action, - initiator: initiator, - responder: responder, - sid: sid.to_owned(), - contents: contents, - reason: reason_element, - other: other, - }) + Ok(Jingle { + action: action, + initiator: initiator, + responder: responder, + sid: sid.to_owned(), + contents: contents, + reason: reason_element, + other: other, + }) + } } -pub fn serialise_content(content: &Content) -> Element { - let mut root = Element::builder("content") - .ns(ns::JINGLE) - .attr("creator", String::from(content.creator.clone())) - .attr("disposition", content.disposition.clone()) - .attr("name", content.name.clone()) - .attr("senders", String::from(content.senders.clone())) - .build(); - root.append_child(content.description.1.clone()); - root.append_child(content.transport.1.clone()); - if let Some(security) = content.security.clone() { - root.append_child(security.1.clone()); +impl<'a> Into for &'a Content { + fn into(self) -> Element { + let mut root = Element::builder("content") + .ns(ns::JINGLE) + .attr("creator", String::from(self.creator.clone())) + .attr("disposition", self.disposition.clone()) + .attr("name", self.name.clone()) + .attr("senders", String::from(self.senders.clone())) + .build(); + root.append_child(self.description.1.clone()); + root.append_child(self.transport.1.clone()); + if let Some(security) = self.security.clone() { + root.append_child(security.1.clone()); + } + root } - root } -pub fn serialise(jingle: &Jingle) -> Element { - let mut root = Element::builder("jingle") - .ns(ns::JINGLE) - .attr("action", String::from(jingle.action.clone())) - .attr("initiator", jingle.initiator.clone()) - .attr("responder", jingle.responder.clone()) - .attr("sid", jingle.sid.clone()) - .build(); - for content in jingle.contents.clone() { - let content_elem = serialise_content(&content); - root.append_child(content_elem); +impl<'a> Into for &'a Jingle { + fn into(self) -> Element { + let mut root = Element::builder("jingle") + .ns(ns::JINGLE) + .attr("action", String::from(self.action.clone())) + .attr("initiator", self.initiator.clone()) + .attr("responder", self.responder.clone()) + .attr("sid", self.sid.clone()) + .build(); + for content in self.contents.clone() { + let content_elem = (&content).into(); + root.append_child(content_elem); + } + if let Some(ref reason) = self.reason { + let reason2: Element = (&reason.reason).into(); + let reason_elem = Element::builder("reason") + .append(reason2) + .append(reason.text.clone()) + .build(); + root.append_child(reason_elem); + } + root } - if let Some(ref reason) = jingle.reason { - let reason_elem = Element::builder("reason") - .append(reason.reason.clone()) - .append(reason.text.clone()) - .build(); - root.append_child(reason_elem); +} + +impl Into for Jingle { + fn into(self) -> Element { + (&self).into() } - root } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use jingle; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.action, jingle::Action::SessionInitiate); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.action, Action::SessionInitiate); assert_eq!(jingle.sid, "coucou"); } #[test] fn test_invalid_jingle() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -442,7 +454,7 @@ mod tests { assert_eq!(message, "Jingle must have an 'action' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -450,7 +462,7 @@ mod tests { assert_eq!(message, "Jingle must have a 'sid' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -461,25 +473,25 @@ mod tests { #[test] fn test_content() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.contents[0].creator, jingle::Creator::Initiator); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, "coucou"); - assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.contents[0].senders, Senders::Both); let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); assert_eq!(jingle.contents[0].disposition, "early-session"); } #[test] fn test_invalid_content() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -487,7 +499,7 @@ mod tests { assert_eq!(message, "Content must have a 'creator' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -495,7 +507,7 @@ mod tests { assert_eq!(message, "Content must have a 'name' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -503,7 +515,7 @@ mod tests { assert_eq!(message, "Unknown creator."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -511,7 +523,7 @@ mod tests { assert_eq!(message, "Unknown senders."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -519,7 +531,7 @@ mod tests { assert_eq!(message, "Unknown senders."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -527,7 +539,7 @@ mod tests { assert_eq!(message, "Content must have one description."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -538,22 +550,22 @@ mod tests { #[test] fn test_reason() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); let reason = jingle.reason.unwrap(); - assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, None); let elem: Element = "coucou".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); let reason = jingle.reason.unwrap(); - assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, Some(String::from("coucou"))); } #[test] fn test_invalid_reason() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -561,7 +573,7 @@ mod tests { assert_eq!(message, "Reason doesn’t contain a valid reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -569,7 +581,7 @@ mod tests { assert_eq!(message, "Unknown reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -577,7 +589,7 @@ mod tests { assert_eq!(message, "Reason contains a foreign element."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -585,7 +597,7 @@ mod tests { assert_eq!(message, "Jingle must not have more than one reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(),