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::stanza_error::DefinedCondition;
8
9generate_element!(
10 /// Acknowledgement of the currently received stanzas.
11 A, "a", SM,
12 attributes: [
13 /// The last handled stanza.
14 h: Required<u32> = "h",
15 ]
16);
17
18impl A {
19 /// Generates a new `<a/>` element.
20 pub fn new(h: u32) -> A {
21 A { h }
22 }
23}
24
25generate_attribute!(
26 /// Whether to allow resumption of a previous stream.
27 ResumeAttr,
28 "resume",
29 bool
30);
31
32generate_element!(
33 /// Client request for enabling stream management.
34 #[derive(Default)]
35 Enable, "enable", SM,
36 attributes: [
37 /// The preferred resumption time in seconds by the client.
38 // TODO: should be the infinite integer set ≥ 1.
39 max: Option<u32> = "max",
40
41 /// Whether the client wants to be allowed to resume the stream.
42 resume: Default<ResumeAttr> = "resume",
43 ]
44);
45
46impl Enable {
47 /// Generates a new `<enable/>` element.
48 pub fn new() -> Self {
49 Enable::default()
50 }
51
52 /// Sets the preferred resumption time in seconds.
53 pub fn with_max(mut self, max: u32) -> Self {
54 self.max = Some(max);
55 self
56 }
57
58 /// Asks for resumption to be possible.
59 pub fn with_resume(mut self) -> Self {
60 self.resume = ResumeAttr::True;
61 self
62 }
63}
64
65generate_id!(
66 /// A random identifier used for stream resumption.
67 StreamId
68);
69
70generate_element!(
71 /// Server response once stream management is enabled.
72 Enabled, "enabled", SM,
73 attributes: [
74 /// A random identifier used for stream resumption.
75 id: Option<StreamId> = "id",
76
77 /// The preferred IP, domain, IP:port or domain:port location for
78 /// resumption.
79 location: Option<String> = "location",
80
81 /// The preferred resumption time in seconds by the server.
82 // TODO: should be the infinite integer set ≥ 1.
83 max: Option<u32> = "max",
84
85 /// Whether stream resumption is allowed.
86 resume: Default<ResumeAttr> = "resume",
87 ]
88);
89
90generate_element!(
91 /// A stream management error happened.
92 Failed, "failed", SM,
93 attributes: [
94 /// The last handled stanza.
95 h: Option<u32> = "h",
96 ],
97 children: [
98 /// The error returned.
99 // XXX: implement the * handling.
100 error: Option<DefinedCondition> = ("*", XMPP_STANZAS) => DefinedCondition
101 ]
102);
103
104generate_empty_element!(
105 /// Requests the currently received stanzas by the other party.
106 R,
107 "r",
108 SM
109);
110
111generate_element!(
112 /// Requests a stream resumption.
113 Resume, "resume", SM,
114 attributes: [
115 /// The last handled stanza.
116 h: Required<u32> = "h",
117
118 /// The previous id given by the server on
119 /// [enabled](struct.Enabled.html).
120 previd: Required<StreamId> = "previd",
121 ]
122);
123
124generate_element!(
125 /// The response by the server for a successfully resumed stream.
126 Resumed, "resumed", SM,
127 attributes: [
128 /// The last handled stanza.
129 h: Required<u32> = "h",
130
131 /// The previous id given by the server on
132 /// [enabled](struct.Enabled.html).
133 previd: Required<StreamId> = "previd",
134 ]
135);
136
137// TODO: add support for optional and required.
138generate_empty_element!(
139 /// Represents availability of Stream Management in `<stream:features/>`.
140 StreamManagement,
141 "sm",
142 SM
143);
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 use crate::Element;
149 use std::convert::TryFrom;
150
151 #[cfg(target_pointer_width = "32")]
152 #[test]
153 fn test_size() {
154 assert_size!(A, 4);
155 assert_size!(ResumeAttr, 1);
156 assert_size!(Enable, 12);
157 assert_size!(StreamId, 12);
158 assert_size!(Enabled, 36);
159 assert_size!(Failed, 12);
160 assert_size!(R, 0);
161 assert_size!(Resume, 16);
162 assert_size!(Resumed, 16);
163 assert_size!(StreamManagement, 0);
164 }
165
166 #[cfg(target_pointer_width = "64")]
167 #[test]
168 fn test_size() {
169 assert_size!(A, 4);
170 assert_size!(ResumeAttr, 1);
171 assert_size!(Enable, 12);
172 assert_size!(StreamId, 24);
173 assert_size!(Enabled, 64);
174 assert_size!(Failed, 12);
175 assert_size!(R, 0);
176 assert_size!(Resume, 32);
177 assert_size!(Resumed, 32);
178 assert_size!(StreamManagement, 0);
179 }
180
181 #[test]
182 fn a() {
183 let elem: Element = "<a xmlns='urn:xmpp:sm:3' h='5'/>".parse().unwrap();
184 let a = A::try_from(elem).unwrap();
185 assert_eq!(a.h, 5);
186 }
187
188 #[test]
189 fn stream_feature() {
190 let elem: Element = "<sm xmlns='urn:xmpp:sm:3'/>".parse().unwrap();
191 StreamManagement::try_from(elem).unwrap();
192 }
193
194 #[test]
195 fn resume() {
196 let elem: Element = "<enable xmlns='urn:xmpp:sm:3' resume='true'/>"
197 .parse()
198 .unwrap();
199 let enable = Enable::try_from(elem).unwrap();
200 assert_eq!(enable.max, None);
201 assert_eq!(enable.resume, ResumeAttr::True);
202
203 let elem: Element = "<enabled xmlns='urn:xmpp:sm:3' resume='true' id='coucou' max='600'/>"
204 .parse()
205 .unwrap();
206 let enabled = Enabled::try_from(elem).unwrap();
207 let previd = enabled.id.unwrap();
208 assert_eq!(enabled.resume, ResumeAttr::True);
209 assert_eq!(previd, StreamId(String::from("coucou")));
210 assert_eq!(enabled.max, Some(600));
211 assert_eq!(enabled.location, None);
212
213 let elem: Element = "<resume xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>"
214 .parse()
215 .unwrap();
216 let resume = Resume::try_from(elem).unwrap();
217 assert_eq!(resume.h, 5);
218 assert_eq!(resume.previd, previd);
219
220 let elem: Element = "<resumed xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>"
221 .parse()
222 .unwrap();
223 let resumed = Resumed::try_from(elem).unwrap();
224 assert_eq!(resumed.h, 5);
225 assert_eq!(resumed.previd, previd);
226 }
227
228 #[test]
229 fn test_serialize_failed() {
230 let reference: Element = "<failed xmlns='urn:xmpp:sm:3'><unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></failed>"
231 .parse()
232 .unwrap();
233
234 let elem: Element = "<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
235 .parse()
236 .unwrap();
237
238 let error = DefinedCondition::try_from(elem).unwrap();
239
240 let failed = Failed {
241 h: None,
242 error: Some(error),
243 };
244 let serialized: Element = failed.into();
245 assert_eq!(serialized, reference);
246 }
247}