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}