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