disco: Split Feature and Identity parsing out of DiscoQueryResult.

Emmanuel Gil Peyrot created

Change summary

src/disco.rs | 74 +++++++++++++++++++++++++++++++++++++----------------
1 file changed, 52 insertions(+), 22 deletions(-)

Detailed changes

src/disco.rs 🔗

@@ -54,6 +54,27 @@ pub struct Feature {
     pub var: String,
 }
 
+impl TryFrom<Element> for Feature {
+    type Err = Error;
+
+    fn try_from(elem: Element) -> Result<Feature, Error> {
+        if !elem.is("feature", ns::DISCO_INFO) {
+            return Err(Error::ParseError("This is not a disco#info feature element."));
+        }
+        for _ in elem.children() {
+            return Err(Error::ParseError("Unknown child in disco#info feature element."));
+        }
+        for (attr, _) in elem.attrs() {
+            if attr != "var" {
+                return Err(Error::ParseError("Unknown attribute in disco#info feature element."));
+            }
+        }
+        Ok(Feature {
+            var: get_attr!(elem, "var", required)
+        })
+    }
+}
+
 impl From<Feature> for Element {
     fn from(feature: Feature) -> Element {
         Element::builder("feature")
@@ -71,6 +92,33 @@ pub struct Identity {
     pub name: Option<String>,
 }
 
+impl TryFrom<Element> for Identity {
+    type Err = Error;
+
+    fn try_from(elem: Element) -> Result<Identity, Error> {
+        if !elem.is("identity", ns::DISCO_INFO) {
+            return Err(Error::ParseError("This is not a disco#info identity element."));
+        }
+
+        let category = get_attr!(elem, "category", required);
+        if category == "" {
+            return Err(Error::ParseError("Identity must have a non-empty 'category' attribute."))
+        }
+
+        let type_ = get_attr!(elem, "type", required);
+        if type_ == "" {
+            return Err(Error::ParseError("Identity must have a non-empty 'type' attribute."))
+        }
+
+        Ok(Identity {
+            category: category,
+            type_: type_,
+            lang: get_attr!(elem, "xml:lang", optional),
+            name: get_attr!(elem, "name", optional),
+        })
+    }
+}
+
 impl From<Identity> for Element {
     fn from(identity: Identity) -> Element {
         Element::builder("identity")
@@ -108,29 +156,11 @@ impl TryFrom<Element> for DiscoInfoResult {
 
         for child in elem.children() {
             if child.is("feature", ns::DISCO_INFO) {
-                let feature = get_attr!(child, "var", required);
-                result.features.push(Feature {
-                    var: feature,
-                });
+                let feature = Feature::try_from(child.clone())?;
+                result.features.push(feature);
             } else if child.is("identity", ns::DISCO_INFO) {
-                let category = get_attr!(child, "category", required);
-                if category == "" {
-                    return Err(Error::ParseError("Identity must have a non-empty 'category' attribute."))
-                }
-
-                let type_ = get_attr!(child, "type", required);
-                if type_ == "" {
-                    return Err(Error::ParseError("Identity must have a non-empty 'type' attribute."))
-                }
-
-                let lang = get_attr!(child, "xml:lang", optional);
-                let name = get_attr!(child, "name", optional);
-                result.identities.push(Identity {
-                    category: category,
-                    type_: type_,
-                    lang: lang,
-                    name: name,
-                });
+                let identity = Identity::try_from(child.clone())?;
+                result.identities.push(identity);
             } else if child.is("x", ns::DATA_FORMS) {
                 let data_form = DataForm::try_from(child.clone())?;
                 if data_form.type_ != DataFormType::Result_ {