Add a Bind parser.

Emmanuel Gil Peyrot created

Change summary

src/bind.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/lib.rs  |  2 +
src/ns.rs   |  2 +
3 files changed, 97 insertions(+)

Detailed changes

src/bind.rs 🔗

@@ -0,0 +1,93 @@
+// Copyright (c) 2018 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// 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::str::FromStr;
+use try_from::TryFrom;
+
+use minidom::Element;
+
+use error::Error;
+use jid::Jid;
+use ns;
+
+#[derive(Debug, Clone)]
+pub struct Bind {
+    pub resource: Option<String>,
+    pub jid: Option<Jid>,
+}
+
+impl Bind {
+    pub fn new(resource: Option<String>) -> Bind {
+        Bind {
+            resource,
+            jid: None,
+        }
+    }
+}
+
+impl TryFrom<Element> for Bind {
+    type Err = Error;
+
+    fn try_from(elem: Element) -> Result<Bind, Error> {
+        check_self!(elem, "bind", ns::BIND);
+        check_no_attributes!(elem, "bind");
+
+        let mut bind = Bind {
+            resource: None,
+            jid: None,
+        };
+        let mut already_set = false;
+        for child in elem.children() {
+            if already_set {
+                return Err(Error::ParseError("Bind can only have one child."));
+            }
+            if child.is("resource", ns::BIND) {
+                check_no_children!(child, "resource");
+                bind.resource = Some(child.text());
+                already_set = true;
+            } else if child.is("jid", ns::BIND) {
+                check_no_children!(child, "jid");
+                bind.jid = Some(Jid::from_str(&child.text())?);
+                already_set = true;
+            } else {
+                return Err(Error::ParseError("Unknown element in bind."));
+            }
+        }
+
+        Ok(bind)
+    }
+}
+
+impl From<Bind> for Element {
+    fn from(bind: Bind) -> Element {
+        Element::builder("bind")
+                .ns(ns::BIND)
+                .append(bind.resource.map(|resource|
+                     Element::builder("resource")
+                             .ns(ns::BIND)
+                             .append(resource)
+                             .build()))
+                .append(bind.jid.map(|jid|
+                     Element::builder("jid")
+                             .ns(ns::BIND)
+                             .append(jid)
+                             .build()))
+                .build()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_simple() {
+        let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>".parse().unwrap();
+        let bind = Bind::try_from(elem).unwrap();
+        assert_eq!(bind.resource, None);
+        assert_eq!(bind.jid, None);
+    }
+}

src/lib.rs 🔗

@@ -57,6 +57,8 @@ pub mod iq;
 pub mod stanza_error;
 /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
 pub mod sasl;
+/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
+pub mod bind;
 
 /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
 pub mod roster;

src/ns.rs 🔗

@@ -11,6 +11,8 @@ pub const JABBER_CLIENT: &str = "jabber:client";
 pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas";
 /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
 pub const SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl";
+/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
+pub const BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind";
 
 /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
 pub const ROSTER: &str = "jabber:iq:roster";