rsm.rs

  1// Copyright (c) 2017 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::util::error::Error;
  8use crate::ns;
  9use minidom::Element;
 10use std::convert::TryFrom;
 11
 12/// Requests paging through a potentially big set of items (represented by an
 13/// UID).
 14#[derive(Debug, Clone)]
 15pub struct SetQuery {
 16    /// Limit the number of items, or use the recipient’s defaults if None.
 17    pub max: Option<usize>,
 18
 19    /// The UID after which to give results, or if None it is the element
 20    /// “before” the first item, effectively an index of negative one.
 21    pub after: Option<String>,
 22
 23    /// The UID before which to give results, or if None it starts with the
 24    /// last page of the full set.
 25    pub before: Option<String>,
 26
 27    /// Numerical index of the page (deprecated).
 28    pub index: Option<usize>,
 29}
 30
 31impl TryFrom<Element> for SetQuery {
 32    type Error = Error;
 33
 34    fn try_from(elem: Element) -> Result<SetQuery, Error> {
 35        check_self!(elem, "set", RSM, "RSM set");
 36        let mut set = SetQuery {
 37            max: None,
 38            after: None,
 39            before: None,
 40            index: None,
 41        };
 42        for child in elem.children() {
 43            if child.is("max", ns::RSM) {
 44                if set.max.is_some() {
 45                    return Err(Error::ParseError("Set can’t have more than one max."));
 46                }
 47                set.max = Some(child.text().parse()?);
 48            } else if child.is("after", ns::RSM) {
 49                if set.after.is_some() {
 50                    return Err(Error::ParseError("Set can’t have more than one after."));
 51                }
 52                set.after = Some(child.text());
 53            } else if child.is("before", ns::RSM) {
 54                if set.before.is_some() {
 55                    return Err(Error::ParseError("Set can’t have more than one before."));
 56                }
 57                set.before = Some(child.text());
 58            } else if child.is("index", ns::RSM) {
 59                if set.index.is_some() {
 60                    return Err(Error::ParseError("Set can’t have more than one index."));
 61                }
 62                set.index = Some(child.text().parse()?);
 63            } else {
 64                return Err(Error::ParseError("Unknown child in set element."));
 65            }
 66        }
 67        Ok(set)
 68    }
 69}
 70
 71impl From<SetQuery> for Element {
 72    fn from(set: SetQuery) -> Element {
 73        Element::builder("set")
 74            .ns(ns::RSM)
 75            .append_all(set.max.map(|max| {
 76                Element::builder("max")
 77                    .ns(ns::RSM)
 78                    .append(format!("{}", max))
 79            }))
 80            .append_all(
 81                set.after
 82                    .map(|after| Element::builder("after").ns(ns::RSM).append(after))
 83            )
 84            .append_all(set.before.map(|before| {
 85                Element::builder("before")
 86                    .ns(ns::RSM)
 87                    .append(before)
 88            }))
 89            .append_all(set.index.map(|index| {
 90                Element::builder("index")
 91                    .ns(ns::RSM)
 92                    .append(format!("{}", index))
 93            }))
 94            .build()
 95    }
 96}
 97
 98/// Describes the paging result of a [query](struct.SetQuery.html).
 99#[derive(Debug, Clone)]
100pub struct SetResult {
101    /// The UID of the first item of the page.
102    pub first: Option<String>,
103
104    /// The position of the [first item](#structfield.first) in the full set
105    /// (which may be approximate).
106    pub first_index: Option<usize>,
107
108    /// The UID of the last item of the page.
109    pub last: Option<String>,
110
111    /// How many items there are in the full set (which may be approximate).
112    pub count: Option<usize>,
113}
114
115impl TryFrom<Element> for SetResult {
116    type Error = Error;
117
118    fn try_from(elem: Element) -> Result<SetResult, Error> {
119        check_self!(elem, "set", RSM, "RSM set");
120        let mut set = SetResult {
121            first: None,
122            first_index: None,
123            last: None,
124            count: None,
125        };
126        for child in elem.children() {
127            if child.is("first", ns::RSM) {
128                if set.first.is_some() {
129                    return Err(Error::ParseError("Set can’t have more than one first."));
130                }
131                set.first_index = get_attr!(child, "index", Option);
132                set.first = Some(child.text());
133            } else if child.is("last", ns::RSM) {
134                if set.last.is_some() {
135                    return Err(Error::ParseError("Set can’t have more than one last."));
136                }
137                set.last = Some(child.text());
138            } else if child.is("count", ns::RSM) {
139                if set.count.is_some() {
140                    return Err(Error::ParseError("Set can’t have more than one count."));
141                }
142                set.count = Some(child.text().parse()?);
143            } else {
144                return Err(Error::ParseError("Unknown child in set element."));
145            }
146        }
147        Ok(set)
148    }
149}
150
151impl From<SetResult> for Element {
152    fn from(set: SetResult) -> Element {
153        let first = set.first.clone().map(|first| {
154            Element::builder("first")
155                .ns(ns::RSM)
156                .attr("index", set.first_index)
157                .append(first)
158        });
159        Element::builder("set")
160            .ns(ns::RSM)
161            .append_all(first)
162            .append_all(
163                set.last
164                    .map(|last| Element::builder("last").ns(ns::RSM).append(last)),
165            )
166            .append_all(set.count.map(|count| {
167                Element::builder("count")
168                    .ns(ns::RSM)
169                    .append(format!("{}", count))
170            }))
171            .build()
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use crate::util::compare_elements::NamespaceAwareCompare;
179
180    #[cfg(target_pointer_width = "32")]
181    #[test]
182    fn test_size() {
183        assert_size!(SetQuery, 40);
184        assert_size!(SetResult, 40);
185    }
186
187    #[cfg(target_pointer_width = "64")]
188    #[test]
189    fn test_size() {
190        assert_size!(SetQuery, 80);
191        assert_size!(SetResult, 80);
192    }
193
194    #[test]
195    fn test_simple() {
196        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
197            .parse()
198            .unwrap();
199        let set = SetQuery::try_from(elem).unwrap();
200        assert_eq!(set.max, None);
201        assert_eq!(set.after, None);
202        assert_eq!(set.before, None);
203        assert_eq!(set.index, None);
204
205        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
206            .parse()
207            .unwrap();
208        let set = SetResult::try_from(elem).unwrap();
209        match set.first {
210            Some(_) => panic!(),
211            None => (),
212        }
213        assert_eq!(set.last, None);
214        assert_eq!(set.count, None);
215    }
216
217    #[test]
218    fn test_unknown() {
219        let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
220            .parse()
221            .unwrap();
222        let error = SetQuery::try_from(elem).unwrap_err();
223        let message = match error {
224            Error::ParseError(string) => string,
225            _ => panic!(),
226        };
227        assert_eq!(message, "This is not a RSM set element.");
228
229        let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
230            .parse()
231            .unwrap();
232        let error = SetResult::try_from(elem).unwrap_err();
233        let message = match error {
234            Error::ParseError(string) => string,
235            _ => panic!(),
236        };
237        assert_eq!(message, "This is not a RSM set element.");
238    }
239
240    #[test]
241    fn test_invalid_child() {
242        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
243            .parse()
244            .unwrap();
245        let error = SetQuery::try_from(elem).unwrap_err();
246        let message = match error {
247            Error::ParseError(string) => string,
248            _ => panic!(),
249        };
250        assert_eq!(message, "Unknown child in set element.");
251
252        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
253            .parse()
254            .unwrap();
255        let error = SetResult::try_from(elem).unwrap_err();
256        let message = match error {
257            Error::ParseError(string) => string,
258            _ => panic!(),
259        };
260        assert_eq!(message, "Unknown child in set element.");
261    }
262
263    #[test]
264    fn test_serialise() {
265        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
266            .parse()
267            .unwrap();
268        let rsm = SetQuery {
269            max: None,
270            after: None,
271            before: None,
272            index: None,
273        };
274        let elem2 = rsm.into();
275        assert_eq!(elem, elem2);
276
277        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
278            .parse()
279            .unwrap();
280        let rsm = SetResult {
281            first: None,
282            first_index: None,
283            last: None,
284            count: None,
285        };
286        let elem2 = rsm.into();
287        assert_eq!(elem, elem2);
288    }
289
290    #[test]
291    fn test_first_index() {
292        let elem: Element =
293            "<set xmlns='http://jabber.org/protocol/rsm'><first index='4'>coucou</first></set>"
294                .parse()
295                .unwrap();
296        let elem1 = elem.clone();
297        let set = SetResult::try_from(elem).unwrap();
298        assert_eq!(set.first, Some(String::from("coucou")));
299        assert_eq!(set.first_index, Some(4));
300
301        let set2 = SetResult {
302            first: Some(String::from("coucou")),
303            first_index: Some(4),
304            last: None,
305            count: None,
306        };
307        let elem2 = set2.into();
308        assert!(elem1.compare_to(&elem2));
309    }
310}