bind.rs

  1// Copyright (c) 2018 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use std::str::FromStr;
  8use try_from::TryFrom;
  9
 10use minidom::Element;
 11
 12use crate::error::Error;
 13use jid::Jid;
 14use crate::ns;
 15use crate::iq::{IqSetPayload, IqResultPayload};
 16
 17/// The request for resource binding, which is the process by which a client
 18/// can obtain a full JID and start exchanging on the XMPP network.
 19///
 20/// See https://xmpp.org/rfcs/rfc6120.html#bind
 21#[derive(Debug, Clone, PartialEq)]
 22pub enum Bind {
 23    /// Requests no particular resource, a random one will be affected by the
 24    /// server.
 25    None,
 26
 27    /// Requests this resource, the server may associate another one though.
 28    Resource(String),
 29
 30    /// The full JID returned by the server for this client.
 31    Jid(Jid),
 32}
 33
 34impl Bind {
 35    /// Creates a resource binding request.
 36    pub fn new(resource: Option<String>) -> Bind {
 37        match resource {
 38            None => Bind::None,
 39            Some(resource) => Bind::Resource(resource),
 40        }
 41    }
 42}
 43
 44impl IqSetPayload for Bind {}
 45impl IqResultPayload for Bind {}
 46
 47impl TryFrom<Element> for Bind {
 48    type Err = Error;
 49
 50    fn try_from(elem: Element) -> Result<Bind, Error> {
 51        check_self!(elem, "bind", BIND);
 52        check_no_attributes!(elem, "bind");
 53
 54        let mut bind = Bind::None;
 55        for child in elem.children() {
 56            if bind != Bind::None {
 57                return Err(Error::ParseError("Bind can only have one child."));
 58            }
 59            if child.is("resource", ns::BIND) {
 60                check_no_children!(child, "resource");
 61                bind = Bind::Resource(child.text());
 62            } else if child.is("jid", ns::BIND) {
 63                check_no_children!(child, "jid");
 64                bind = Bind::Jid(Jid::from_str(&child.text())?);
 65            } else {
 66                return Err(Error::ParseError("Unknown element in bind."));
 67            }
 68        }
 69
 70        Ok(bind)
 71    }
 72}
 73
 74impl From<Bind> for Element {
 75    fn from(bind: Bind) -> Element {
 76        Element::builder("bind")
 77                .ns(ns::BIND)
 78                .append(match bind {
 79                     Bind::None => vec!(),
 80                     Bind::Resource(resource) => vec!(
 81                         Element::builder("resource")
 82                                 .ns(ns::BIND)
 83                                 .append(resource)
 84                                 .build()
 85                     ),
 86                     Bind::Jid(jid) => vec!(
 87                         Element::builder("jid")
 88                                 .ns(ns::BIND)
 89                                 .append(jid)
 90                                 .build()
 91                     ),
 92                 })
 93                .build()
 94    }
 95}
 96
 97#[cfg(test)]
 98mod tests {
 99    use super::*;
100
101    #[cfg(target_pointer_width = "32")]
102    #[test]
103    fn test_size() {
104        assert_size!(Bind, 40);
105    }
106
107    #[cfg(target_pointer_width = "64")]
108    #[test]
109    fn test_size() {
110        assert_size!(Bind, 80);
111    }
112
113    #[test]
114    fn test_simple() {
115        let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>".parse().unwrap();
116        let bind = Bind::try_from(elem).unwrap();
117        assert_eq!(bind, Bind::None);
118    }
119}