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