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 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}