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