1use xso::{AsXml, FromXml};
8
9use crate::data_forms::DataForm;
10use crate::date::DateTime;
11use crate::message::MessagePayload;
12use crate::ns;
13use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId};
14use jid::Jid;
15use minidom::Element;
16
17#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
19#[xml(namespace = ns::PUBSUB_EVENT, name = "item")]
20pub struct Item {
21 #[xml(attribute(default))]
23 pub id: Option<ItemId>,
24
25 #[xml(attribute(default))]
27 pub publisher: Option<Jid>,
28
29 #[xml(element(default))]
31 pub payload: Option<Element>,
32}
33
34#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
36#[xml(namespace = ns::PUBSUB_EVENT, name = "event")]
37pub struct Event {
38 #[xml(child)]
40 pub payload: Payload,
41}
42
43impl MessagePayload for Event {}
44
45#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
47#[xml(namespace = ns::PUBSUB_EVENT, exhaustive)]
48pub enum Payload {
49 #[xml(name = "configuration")]
55 Configuration {
56 #[xml(attribute)]
58 node: NodeName,
59
60 #[xml(child(default))]
62 form: Option<DataForm>,
63 },
64
65 #[xml(name = "delete")]
67 Delete {
68 #[xml(attribute)]
70 node: NodeName,
71
72 #[xml(extract(default, fields(attribute(default, name = "uri"))))]
74 redirect: Option<String>,
75 },
76
77 #[xml(name = "items")]
79 Items {
80 #[xml(attribute)]
82 node: NodeName,
83
84 #[xml(child(n = ..))]
86 published: Vec<Item>,
87
88 #[xml(extract(n = .., name = "retract", fields(attribute(name = "id", type_ = ItemId))))]
90 retracted: Vec<ItemId>,
91 },
92
93 #[xml(name = "purge")]
95 Purge {
96 #[xml(attribute)]
98 node: NodeName,
99 },
100
101 #[xml(name = "subscription")]
103 Subscription {
104 #[xml(attribute)]
106 node: NodeName,
107
108 #[xml(attribute(default))]
110 expiry: Option<DateTime>,
111
112 #[xml(attribute(default))]
114 jid: Option<Jid>,
115
116 #[xml(attribute(default))]
118 subid: Option<SubscriptionId>,
119
120 #[xml(attribute(default))]
122 subscription: Option<Subscription>,
123 },
124}
125
126impl Payload {
127 pub fn node_name(&self) -> &NodeName {
129 match self {
130 Self::Purge { node, .. } => node,
131 Self::Items { node, .. } => node,
132 Self::Subscription { node, .. } => node,
133 Self::Delete { node, .. } => node,
134 Self::Configuration { node, .. } => node,
135 }
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use jid::BareJid;
143 use xso::error::{Error, FromElementError};
144
145 #[test]
147 #[ignore]
148 fn missing_items() {
149 let elem: Element =
150 "<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='coucou'/></event>"
151 .parse()
152 .unwrap();
153 let error = Event::try_from(elem).unwrap_err();
154 let message = match error {
155 FromElementError::Invalid(Error::Other(string)) => string,
156 _ => panic!(),
157 };
158 assert_eq!(message, "Missing children in items element.");
159 }
160
161 #[test]
162 fn test_simple_items() {
163 let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='coucou'><item id='test' publisher='test@coucou'/></items></event>".parse().unwrap();
164 let event = Event::try_from(elem).unwrap();
165 match event.payload {
166 Payload::Items {
167 node,
168 published,
169 retracted,
170 } => {
171 assert_eq!(node, NodeName(String::from("coucou")));
172 assert_eq!(retracted.len(), 0);
173 assert_eq!(published[0].id, Some(ItemId(String::from("test"))));
174 assert_eq!(
175 published[0].publisher.clone().unwrap(),
176 BareJid::new("test@coucou").unwrap()
177 );
178 assert_eq!(published[0].payload, None);
179 }
180 _ => panic!(),
181 }
182 }
183
184 #[test]
185 fn test_simple_pep() {
186 let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='something'><item><foreign xmlns='example:namespace'/></item></items></event>".parse().unwrap();
187 let event = Event::try_from(elem).unwrap();
188 match event.payload {
189 Payload::Items {
190 node,
191 published,
192 retracted,
193 } => {
194 assert_eq!(node, NodeName(String::from("something")));
195 assert_eq!(retracted.len(), 0);
196 assert_eq!(published[0].id, None);
197 assert_eq!(published[0].publisher, None);
198 match published[0].payload {
199 Some(ref elem) => assert!(elem.is("foreign", "example:namespace")),
200 _ => panic!(),
201 }
202 }
203 _ => panic!(),
204 }
205 }
206
207 #[test]
208 fn test_simple_retract() {
209 let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='something'><retract id='coucou'/><retract id='test'/></items></event>".parse().unwrap();
210 let event = Event::try_from(elem).unwrap();
211 match event.payload {
212 Payload::Items {
213 node,
214 published,
215 retracted,
216 } => {
217 assert_eq!(node, NodeName(String::from("something")));
218 assert_eq!(published.len(), 0);
219 assert_eq!(retracted[0], ItemId(String::from("coucou")));
220 assert_eq!(retracted[1], ItemId(String::from("test")));
221 }
222 _ => panic!(),
223 }
224 }
225
226 #[test]
227 fn test_simple_delete() {
228 let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><delete node='coucou'><redirect uri='hello'/></delete></event>".parse().unwrap();
229 let event = Event::try_from(elem).unwrap();
230 match event.payload {
231 Payload::Delete { node, redirect } => {
232 assert_eq!(node, NodeName(String::from("coucou")));
233 assert_eq!(redirect, Some(String::from("hello")));
234 }
235 _ => panic!(),
236 }
237 }
238
239 #[test]
240 fn test_simple_purge() {
241 let elem: Element =
242 "<event xmlns='http://jabber.org/protocol/pubsub#event'><purge node='coucou'/></event>"
243 .parse()
244 .unwrap();
245 let event = Event::try_from(elem).unwrap();
246 match event.payload {
247 Payload::Purge { node } => {
248 assert_eq!(node, NodeName(String::from("coucou")));
249 }
250 _ => panic!(),
251 }
252 }
253
254 #[test]
255 fn test_simple_configure() {
256 let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><configuration node='coucou'><x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>http://jabber.org/protocol/pubsub#node_config</value></field></x></configuration></event>".parse().unwrap();
257 let event = Event::try_from(elem).unwrap();
258 match event.payload {
259 Payload::Configuration { node, form: _ } => {
260 assert_eq!(node, NodeName(String::from("coucou")));
261 }
263 _ => panic!(),
264 }
265 }
266
267 #[test]
268 fn test_invalid() {
269 let elem: Element =
270 "<event xmlns='http://jabber.org/protocol/pubsub#event'><coucou node='test'/></event>"
271 .parse()
272 .unwrap();
273 let error = Event::try_from(elem).unwrap_err();
274 let message = match error {
275 FromElementError::Invalid(Error::Other(string)) => string,
276 _ => panic!(),
277 };
278 assert_eq!(message, "This is not a Payload element.");
279 }
280
281 #[cfg(not(feature = "disable-validation"))]
282 #[test]
283 fn test_invalid_attribute() {
284 let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event' coucou=''/>"
285 .parse()
286 .unwrap();
287 let error = Event::try_from(elem).unwrap_err();
288 let message = match error {
289 FromElementError::Invalid(Error::Other(string)) => string,
290 _ => panic!(),
291 };
292 assert_eq!(message, "Unknown attribute in Event element.");
293 }
294
295 #[test]
296 fn test_ex221_subscription() {
297 let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><subscription expiry='2006-02-28T23:59:59+00:00' jid='francisco@denmark.lit' node='princely_musings' subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/></event>"
298 .parse()
299 .unwrap();
300 let event = Event::try_from(elem.clone()).unwrap();
301 match event.payload.clone() {
302 Payload::Subscription {
303 node,
304 expiry,
305 jid,
306 subid,
307 subscription,
308 } => {
309 assert_eq!(node, NodeName(String::from("princely_musings")));
310 assert_eq!(
311 subid,
312 Some(SubscriptionId(String::from(
313 "ba49252aaa4f5d320c24d3766f0bdcade78c78d3"
314 )))
315 );
316 assert_eq!(subscription, Some(Subscription::Subscribed));
317 assert_eq!(jid.unwrap(), BareJid::new("francisco@denmark.lit").unwrap());
318 assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap()));
319 }
320 _ => panic!(),
321 }
322
323 let elem2: Element = event.into();
324 assert_eq!(elem, elem2);
325 }
326}