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}