From 2b9a6d57b63538b3e6d3627d92cfc995a89362c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:40:04 +0100 Subject: [PATCH] jingle: Support more than one with different @xml:lang. --- src/jingle.rs | 64 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 2c6c3fb..9edb318 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -9,6 +9,7 @@ use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; use minidom::Element; +use std::collections::BTreeMap; use std::str::FromStr; use try_from::TryFrom; @@ -354,6 +355,8 @@ impl From for Element { } } +type Lang = String; + /// Informs the recipient of something. #[derive(Debug, Clone)] pub struct ReasonElement { @@ -361,7 +364,7 @@ pub struct ReasonElement { pub reason: Reason, /// A human-readable description of this reason. - pub text: Option, + pub texts: BTreeMap, } impl TryFrom for ReasonElement { @@ -369,37 +372,38 @@ impl TryFrom for ReasonElement { fn try_from(elem: Element) -> Result { check_self!(elem, "reason", JINGLE); + check_no_attributes!(elem, "reason"); let mut reason = None; - let mut text = None; + let mut texts = BTreeMap::new(); for child in elem.children() { - if !child.has_ns(ns::JINGLE) { + if child.is("text", ns::JINGLE) { + check_no_children!(child, "text"); + check_no_unknown_attributes!(child, "text", ["xml:lang"]); + let lang = get_attr!(elem, "xml:lang", Default); + if texts.insert(lang, child.text()).is_some() { + return Err(Error::ParseError( + "Text element present twice for the same xml:lang.", + )); + } + } else if child.has_ns(ns::JINGLE) { + if reason.is_some() { + return Err(Error::ParseError( + "Reason must not have more than one reason.", + )); + } + check_no_children!(child, "reason"); + check_no_attributes!(child, "reason"); + reason = Some(child.name().parse()?); + } else { return Err(Error::ParseError("Reason contains a foreign element.")); } - match child.name() { - "text" => { - if text.is_some() { - return Err(Error::ParseError( - "Reason must not have more than one text.", - )); - } - text = Some(child.text()); - } - name => { - if reason.is_some() { - return Err(Error::ParseError( - "Reason must not have more than one reason.", - )); - } - reason = Some(name.parse()?); - } - } } let reason = reason.ok_or(Error::ParseError( "Reason doesn’t contain a valid reason.", ))?; Ok(ReasonElement { reason, - text, + texts, }) } } @@ -407,8 +411,16 @@ impl TryFrom for ReasonElement { impl From for Element { fn from(reason: ReasonElement) -> Element { Element::builder("reason") + .ns(ns::JINGLE) .append(Element::from(reason.reason)) - .append(reason.text) + .append( + reason.texts.into_iter().map(|(lang, text)| { + Element::builder("text") + .ns(ns::JINGLE) + .attr("xml:lang", lang) + .append(text) + .build() + }).collect::>()) .build() } } @@ -679,13 +691,13 @@ mod tests { let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); - assert_eq!(reason.text, None); + assert_eq!(reason.texts, BTreeMap::new()); let elem: Element = "coucou".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); - assert_eq!(reason.text, Some(String::from("coucou"))); + assert_eq!(reason.texts.get(""), Some(&String::from("coucou"))); } #[test] @@ -728,6 +740,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Reason must not have more than one text."); + assert_eq!(message, "Text element present twice for the same xml:lang."); } }