jingle_ft: Improve <range/> parsing.

Emmanuel Gil Peyrot created

Change summary

src/jingle_ft.rs | 81 ++++++++++++++++++++++++++++++++++---------------
1 file changed, 56 insertions(+), 25 deletions(-)

Detailed changes

src/jingle_ft.rs 🔗

@@ -12,7 +12,7 @@ use std::str::FromStr;
 use hashes::Hash;
 use jingle::{Creator, ContentId};
 
-use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter};
+use minidom::{Element, IntoAttributeValue};
 use chrono::{DateTime, FixedOffset};
 
 use error::Error;
@@ -25,17 +25,32 @@ pub struct Range {
     pub hashes: Vec<Hash>,
 }
 
-impl IntoElements for Range {
-    fn into_elements(self, emitter: &mut ElementEmitter) {
-        let mut elem = Element::builder("range")
-                               .ns(ns::JINGLE_FT)
-                               .attr("offset", if self.offset == 0 { None } else { Some(self.offset) })
-                               .attr("length", self.length)
-                               .build();
-        for hash in self.hashes {
-            elem.append_child(hash.into());
+impl TryFrom<Element> for Range {
+    type Err = Error;
+
+    fn try_from(elem: Element) -> Result<Range, Error> {
+        check_self!(elem, "range", ns::JINGLE_FT);
+        check_no_unknown_attributes!(elem, "range", ["offset", "length"]);
+        let mut hashes = vec!();
+        for child in elem.children() {
+            hashes.push(Hash::try_from(child.clone())?);
         }
-        emitter.append_child(elem);
+        Ok(Range {
+            offset: get_attr!(elem, "offset", default),
+            length: get_attr!(elem, "length", optional),
+            hashes: hashes,
+        })
+    }
+}
+
+impl From<Range> for Element {
+    fn from(range: Range) -> Element {
+        Element::builder("range")
+                .ns(ns::JINGLE_FT)
+                .attr("offset", if range.offset == 0 { None } else { Some(range.offset) })
+                .attr("length", range.length)
+                .append(range.hashes)
+                .build()
     }
 }
 
@@ -102,20 +117,7 @@ impl TryFrom<Element> for File {
                 if file.range.is_some() {
                     return Err(Error::ParseError("File must not have more than one range."));
                 }
-                let offset = get_attr!(child, "offset", default);
-                let length = get_attr!(child, "length", optional);
-                let mut range_hashes = vec!();
-                for hash_element in child.children() {
-                    if !hash_element.is("hash", ns::HASHES) {
-                        return Err(Error::ParseError("Unknown element in JingleFT range."));
-                    }
-                    range_hashes.push(Hash::try_from(hash_element.clone())?);
-                }
-                file.range = Some(Range {
-                    offset: offset,
-                    length: length,
-                    hashes: range_hashes,
-                });
+                file.range = Some(Range::try_from(child.clone())?);
             } else if child.is("hash", ns::HASHES) {
                 file.hashes.push(Hash::try_from(child.clone())?);
             } else {
@@ -463,4 +465,33 @@ mod tests {
         };
         assert_eq!(message, "Unknown value for 'creator' attribute.");
     }
+
+    #[test]
+    fn test_range() {
+        let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5'/>".parse().unwrap();
+        let range = Range::try_from(elem).unwrap();
+        assert_eq!(range.offset, 0);
+        assert_eq!(range.length, None);
+        assert_eq!(range.hashes, vec!());
+
+        let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' offset='2048' length='1024'><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>kHp5RSzW/h7Gm1etSf90Mr5PC/k=</hash></range>".parse().unwrap();
+        let hashes = vec!(Hash { algo: Algo::Sha_1, hash: vec!(144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, 11, 249) });
+        let range = Range::try_from(elem).unwrap();
+        assert_eq!(range.offset, 2048);
+        assert_eq!(range.length, Some(1024));
+        assert_eq!(range.hashes, hashes);
+        let elem2 = Element::from(range);
+        let range2 = Range::try_from(elem2).unwrap();
+        assert_eq!(range2.offset, 2048);
+        assert_eq!(range2.length, Some(1024));
+        assert_eq!(range2.hashes, hashes);
+
+        let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' coucou=''/>".parse().unwrap();
+        let error = Range::try_from(elem).unwrap_err();
+        let message = match error {
+            Error::ParseError(string) => string,
+            _ => panic!(),
+        };
+        assert_eq!(message, "Unknown attribute in range element.");
+    }
 }