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(set.max.map(|max| {
 76                Element::builder("max")
 77                    .ns(ns::RSM)
 78                    .append(format!("{}", max))
 79                    .build()
 80            }))
 81            .append(
 82                set.after
 83                    .map(|after| Element::builder("after").ns(ns::RSM).append(after).build()),
 84            )
 85            .append(set.before.map(|before| {
 86                Element::builder("before")
 87                    .ns(ns::RSM)
 88                    .append(before)
 89                    .build()
 90            }))
 91            .append(set.index.map(|index| {
 92                Element::builder("index")
 93                    .ns(ns::RSM)
 94                    .append(format!("{}", index))
 95                    .build()
 96            }))
 97            .build()
 98    }
 99}
100
101/// Describes the paging result of a [query](struct.SetQuery.html).
102#[derive(Debug, Clone)]
103pub struct SetResult {
104    /// The UID of the first item of the page.
105    pub first: Option<String>,
106
107    /// The position of the [first item](#structfield.first) in the full set
108    /// (which may be approximate).
109    pub first_index: Option<usize>,
110
111    /// The UID of the last item of the page.
112    pub last: Option<String>,
113
114    /// How many items there are in the full set (which may be approximate).
115    pub count: Option<usize>,
116}
117
118impl TryFrom<Element> for SetResult {
119    type Error = Error;
120
121    fn try_from(elem: Element) -> Result<SetResult, Error> {
122        check_self!(elem, "set", RSM, "RSM set");
123        let mut set = SetResult {
124            first: None,
125            first_index: None,
126            last: None,
127            count: None,
128        };
129        for child in elem.children() {
130            if child.is("first", ns::RSM) {
131                if set.first.is_some() {
132                    return Err(Error::ParseError("Set can’t have more than one first."));
133                }
134                set.first_index = get_attr!(child, "index", Option);
135                set.first = Some(child.text());
136            } else if child.is("last", ns::RSM) {
137                if set.last.is_some() {
138                    return Err(Error::ParseError("Set can’t have more than one last."));
139                }
140                set.last = Some(child.text());
141            } else if child.is("count", ns::RSM) {
142                if set.count.is_some() {
143                    return Err(Error::ParseError("Set can’t have more than one count."));
144                }
145                set.count = Some(child.text().parse()?);
146            } else {
147                return Err(Error::ParseError("Unknown child in set element."));
148            }
149        }
150        Ok(set)
151    }
152}
153
154impl From<SetResult> for Element {
155    fn from(set: SetResult) -> Element {
156        let first = set.first.clone().map(|first| {
157            Element::builder("first")
158                .ns(ns::RSM)
159                .attr("index", set.first_index)
160                .append(first)
161                .build()
162        });
163        Element::builder("set")
164            .ns(ns::RSM)
165            .append(first)
166            .append(
167                set.last
168                    .map(|last| Element::builder("last").ns(ns::RSM).append(last).build()),
169            )
170            .append(set.count.map(|count| {
171                Element::builder("count")
172                    .ns(ns::RSM)
173                    .append(format!("{}", count))
174                    .build()
175            }))
176            .build()
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183    use crate::util::compare_elements::NamespaceAwareCompare;
184
185    #[cfg(target_pointer_width = "32")]
186    #[test]
187    fn test_size() {
188        assert_size!(SetQuery, 40);
189        assert_size!(SetResult, 40);
190    }
191
192    #[cfg(target_pointer_width = "64")]
193    #[test]
194    fn test_size() {
195        assert_size!(SetQuery, 80);
196        assert_size!(SetResult, 80);
197    }
198
199    #[test]
200    fn test_simple() {
201        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
202            .parse()
203            .unwrap();
204        let set = SetQuery::try_from(elem).unwrap();
205        assert_eq!(set.max, None);
206        assert_eq!(set.after, None);
207        assert_eq!(set.before, None);
208        assert_eq!(set.index, None);
209
210        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
211            .parse()
212            .unwrap();
213        let set = SetResult::try_from(elem).unwrap();
214        match set.first {
215            Some(_) => panic!(),
216            None => (),
217        }
218        assert_eq!(set.last, None);
219        assert_eq!(set.count, None);
220    }
221
222    #[test]
223    fn test_unknown() {
224        let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
225            .parse()
226            .unwrap();
227        let error = SetQuery::try_from(elem).unwrap_err();
228        let message = match error {
229            Error::ParseError(string) => string,
230            _ => panic!(),
231        };
232        assert_eq!(message, "This is not a RSM set element.");
233
234        let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
235            .parse()
236            .unwrap();
237        let error = SetResult::try_from(elem).unwrap_err();
238        let message = match error {
239            Error::ParseError(string) => string,
240            _ => panic!(),
241        };
242        assert_eq!(message, "This is not a RSM set element.");
243    }
244
245    #[test]
246    fn test_invalid_child() {
247        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
248            .parse()
249            .unwrap();
250        let error = SetQuery::try_from(elem).unwrap_err();
251        let message = match error {
252            Error::ParseError(string) => string,
253            _ => panic!(),
254        };
255        assert_eq!(message, "Unknown child in set element.");
256
257        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
258            .parse()
259            .unwrap();
260        let error = SetResult::try_from(elem).unwrap_err();
261        let message = match error {
262            Error::ParseError(string) => string,
263            _ => panic!(),
264        };
265        assert_eq!(message, "Unknown child in set element.");
266    }
267
268    #[test]
269    fn test_serialise() {
270        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
271            .parse()
272            .unwrap();
273        let rsm = SetQuery {
274            max: None,
275            after: None,
276            before: None,
277            index: None,
278        };
279        let elem2 = rsm.into();
280        assert_eq!(elem, elem2);
281
282        let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
283            .parse()
284            .unwrap();
285        let rsm = SetResult {
286            first: None,
287            first_index: None,
288            last: None,
289            count: None,
290        };
291        let elem2 = rsm.into();
292        assert_eq!(elem, elem2);
293    }
294
295    #[test]
296    fn test_first_index() {
297        let elem: Element =
298            "<set xmlns='http://jabber.org/protocol/rsm'><first index='4'>coucou</first></set>"
299                .parse()
300                .unwrap();
301        let elem1 = elem.clone();
302        let set = SetResult::try_from(elem).unwrap();
303        assert_eq!(set.first, Some(String::from("coucou")));
304        assert_eq!(set.first_index, Some(4));
305
306        let set2 = SetResult {
307            first: Some(String::from("coucou")),
308            first_index: Some(4),
309            last: None,
310            count: None,
311        };
312        let elem2 = set2.into();
313        assert!(elem1.compare_to(&elem2));
314    }
315}