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}