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