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_simple() {
163 let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
164 let set = SetQuery::try_from(elem).unwrap();
165 assert_eq!(set.max, None);
166 assert_eq!(set.after, None);
167 assert_eq!(set.before, None);
168 assert_eq!(set.index, None);
169
170 let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
171 let set = SetResult::try_from(elem).unwrap();
172 match set.first {
173 Some(_) => panic!(),
174 None => (),
175 }
176 assert_eq!(set.last, None);
177 assert_eq!(set.count, None);
178 }
179
180 #[test]
181 fn test_unknown() {
182 let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
183 let error = SetQuery::try_from(elem).unwrap_err();
184 let message = match error {
185 Error::ParseError(string) => string,
186 _ => panic!(),
187 };
188 assert_eq!(message, "This is not a RSM set element.");
189
190 let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
191 let error = SetResult::try_from(elem).unwrap_err();
192 let message = match error {
193 Error::ParseError(string) => string,
194 _ => panic!(),
195 };
196 assert_eq!(message, "This is not a RSM set element.");
197 }
198
199 #[test]
200 fn test_invalid_child() {
201 let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>".parse().unwrap();
202 let error = SetQuery::try_from(elem).unwrap_err();
203 let message = match error {
204 Error::ParseError(string) => string,
205 _ => panic!(),
206 };
207 assert_eq!(message, "Unknown child in set element.");
208
209 let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>".parse().unwrap();
210 let error = SetResult::try_from(elem).unwrap_err();
211 let message = match error {
212 Error::ParseError(string) => string,
213 _ => panic!(),
214 };
215 assert_eq!(message, "Unknown child in set element.");
216 }
217
218 #[test]
219 fn test_serialise() {
220 let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
221 let rsm = SetQuery {
222 max: None,
223 after: None,
224 before: None,
225 index: None,
226 };
227 let elem2 = rsm.into();
228 assert_eq!(elem, elem2);
229
230 let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
231 let rsm = SetResult {
232 first: None,
233 first_index: None,
234 last: None,
235 count: None,
236 };
237 let elem2 = rsm.into();
238 assert_eq!(elem, elem2);
239 }
240
241 #[test]
242 fn test_first_index() {
243 let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><first index='4'>coucou</first></set>".parse().unwrap();
244 let elem1 = elem.clone();
245 let set = SetResult::try_from(elem).unwrap();
246 assert_eq!(set.first, Some(String::from("coucou")));
247 assert_eq!(set.first_index, Some(4));
248
249 let set2 = SetResult {
250 first: Some(String::from("coucou")),
251 first_index: Some(4),
252 last: None,
253 count: None,
254 };
255 let elem2 = set2.into();
256 assert!(elem1.compare_to(&elem2));
257 }
258}