1use xso::{AsXml, FromXml};
9
10use crate::muc::user::{Affiliation, Role};
11use crate::ns;
12
13use jid::BareJid;
14
15#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
17#[xml(namespace = ns::MUC_ADMIN, name = "actor")]
18pub struct Actor {
19 #[xml(attribute(default))]
21 jid: Option<BareJid>,
22
23 #[xml(attribute(default))]
25 nick: Option<String>,
26}
27
28generate_elem_id!(
29 Reason,
31 "reason",
32 MUC_ADMIN
33);
34
35#[derive(FromXml, AsXml, Debug, PartialEq, Clone, Default)]
37#[xml(namespace = ns::MUC_ADMIN, name = "item")]
38pub struct Item {
39 #[xml(attribute)]
43 pub affiliation: Option<Affiliation>,
44
45 #[xml(attribute(default))]
47 pub jid: Option<BareJid>,
48
49 #[xml(attribute(default))]
51 pub nick: Option<String>,
52
53 #[xml(attribute)]
55 pub role: Option<Role>,
56
57 #[xml(child(default))]
59 pub actor: Option<Actor>,
60
61 #[xml(child(default))]
63 pub reason: Option<Reason>,
64}
65
66impl Item {
67 pub fn new() -> Item {
69 Item {
70 affiliation: None,
71 role: None,
72 jid: None,
73 nick: None,
74 actor: None,
75 reason: None,
76 }
77 }
78
79 pub fn with_jid(mut self, jid: BareJid) -> Item {
81 self.jid = Some(jid);
82 self
83 }
84
85 pub fn with_nick<S: Into<String>>(mut self, nick: S) -> Item {
87 self.nick = Some(nick.into());
88 self
89 }
90
91 pub fn with_actor(mut self, actor: Actor) -> Item {
93 self.actor = Some(actor);
94 self
95 }
96
97 pub fn with_reason<S: Into<String>>(mut self, reason: S) -> Item {
99 self.reason = Some(Reason(reason.into()));
100 self
101 }
102
103 pub fn with_affiliation(mut self, affiliation: Affiliation) -> Item {
105 self.affiliation = Some(affiliation);
106 self
107 }
108 pub fn with_role(mut self, role: Role) -> Item {
110 self.role = Some(role);
111 self
112 }
113}
114
115#[derive(FromXml, AsXml, Debug, Default, PartialEq, Clone)]
117#[xml(namespace = ns::MUC_ADMIN, name = "query")]
118pub struct MucUser {
119 #[xml(child(n = ..))]
121 pub items: Vec<Item>,
122}
123
124impl MucUser {
125 pub fn new() -> MucUser {
127 MucUser::default()
128 }
129
130 pub fn with_items(mut self, items: Vec<Item>) -> MucUser {
132 self.items = items;
133 self
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::message::Message;
141 use crate::presence::{Presence, Type as PresenceType};
142 use jid::Jid;
143 use minidom::Element;
144 use xso::error::{Error, FromElementError};
145
146 #[cfg(target_pointer_width = "32")]
147 #[test]
148 fn test_size() {
149 assert_size!(Actor, 28);
150 assert_size!(Reason, 12);
151 assert_size!(Affiliation, 1);
152 assert_size!(Role, 1);
153 assert_size!(Item, 84);
154 assert_size!(MucUser, 136);
155 }
156
157 #[cfg(target_pointer_width = "64")]
158 #[test]
159 fn test_size() {
160 assert_size!(Actor, 56);
161 assert_size!(Reason, 24);
162 assert_size!(Affiliation, 1);
163 assert_size!(Role, 1);
164 assert_size!(Item, 144);
165 assert_size!(MucUser, 24);
166 }
167
168 #[test]
169 fn test_simple() {
170 let elem: Element = "<query xmlns='http://jabber.org/protocol/muc#admin'/>"
171 .parse()
172 .unwrap();
173 MucUser::try_from(elem).unwrap();
174 }
175
176 #[test]
177 fn items() {
178 let elem: Element = "<query xmlns='http://jabber.org/protocol/muc#admin'>
179 <item affiliation='member' role='moderator'/>
180 </query>"
181 .parse()
182 .unwrap();
183 let muc_user = MucUser::try_from(elem).unwrap();
184 assert_eq!(muc_user.items.len(), 1);
185 assert_eq!(muc_user.items[0].affiliation, Some(Affiliation::Member));
186 assert_eq!(muc_user.items[0].role, Some(Role::Moderator));
187 }
188
189 #[test]
190 #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
191 fn test_invalid_child() {
192 let elem: Element = "<query xmlns='http://jabber.org/protocol/muc#admin'>
193 <coucou/>
194 </query>"
195 .parse()
196 .unwrap();
197 let error = MucUser::try_from(elem).unwrap_err();
198 let message = match error {
199 FromElementError::Invalid(Error::Other(string)) => string,
200 _ => panic!(),
201 };
202 assert_eq!(message, "Unknown child in MucUser element.");
203 }
204
205 #[test]
206 fn test_serialise() {
207 let elem: Element = "<query xmlns='http://jabber.org/protocol/muc#admin'/>"
208 .parse()
209 .unwrap();
210 let muc = MucUser::new();
211 let elem2 = muc.into();
212 assert_eq!(elem, elem2);
213 }
214
215 #[cfg(feature = "pedantic")]
216 #[test]
217 fn test_invalid_attribute() {
218 let elem: Element = "<query xmlns='http://jabber.org/protocol/muc#admin' coucou=''/>"
219 .parse()
220 .unwrap();
221 let error = MucUser::try_from(elem).unwrap_err();
222 let message = match error {
223 FromElementError::Invalid(Error::Other(string)) => string,
224 _ => panic!(),
225 };
226 assert_eq!(message, "Unknown attribute in MucUser element.");
227 }
228
229 #[test]
232 #[ignore]
233 fn test_actor_required_attributes() {
234 let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#admin'/>"
235 .parse()
236 .unwrap();
237 let error = Actor::try_from(elem).unwrap_err();
238 let message = match error {
239 FromElementError::Invalid(Error::Other(string)) => string,
240 _ => panic!(),
241 };
242 assert_eq!(message, "Either 'jid' or 'nick' attribute is required.");
243 }
244
245 #[test]
246 fn test_actor_jid() {
247 let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#admin'
248 jid='foo@bar'/>"
249 .parse()
250 .unwrap();
251 let actor = Actor::try_from(elem).unwrap();
252 assert_eq!(actor.jid, Some("foo@bar".parse::<BareJid>().unwrap()));
253 assert_eq!(actor.nick, None);
254 }
255
256 #[test]
257 fn test_actor_nick() {
258 let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#admin' nick='baz'/>"
259 .parse()
260 .unwrap();
261 let actor = Actor::try_from(elem).unwrap();
262 assert_eq!(actor.nick, Some("baz".to_owned()));
263 assert_eq!(actor.jid, None);
264 }
265
266 #[test]
267 fn test_reason_simple() {
268 let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#admin'>Reason</reason>"
269 .parse()
270 .unwrap();
271 let elem2 = elem.clone();
272 let reason = Reason::try_from(elem).unwrap();
273 assert_eq!(reason.0, "Reason".to_owned());
274
275 let elem3 = reason.into();
276 assert_eq!(elem2, elem3);
277 }
278
279 #[cfg(feature = "pedantic")]
280 #[test]
281 fn test_reason_invalid_attribute() {
282 let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#admin' foo='bar'/>"
283 .parse()
284 .unwrap();
285 let error = Reason::try_from(elem).unwrap_err();
286 let message = match error {
287 FromElementError::Invalid(Error::Other(string)) => string,
288 _ => panic!(),
289 };
290 assert_eq!(message, "Unknown attribute in Reason element.".to_owned());
291 }
292
293 #[cfg(feature = "pedantic")]
294 #[test]
295 fn test_reason_invalid() {
296 let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#admin'>
297 <foobar/>
298 </reason>"
299 .parse()
300 .unwrap();
301 let error = Reason::try_from(elem).unwrap_err();
302 let message = match error {
303 FromElementError::Invalid(Error::Other(string)) => string,
304 _ => panic!(),
305 };
306 assert_eq!(message, "Unknown child in Reason element.".to_owned());
307 }
308
309 #[cfg(feature = "pedantic")]
310 #[test]
311 fn test_item_invalid_attr() {
312 let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#admin'
313 affiliation='member'
314 role='moderator'
315 foo='bar'/>"
316 .parse()
317 .unwrap();
318 let error = Item::try_from(elem).unwrap_err();
319 let message = match error {
320 FromElementError::Invalid(Error::Other(string)) => string,
321 _ => panic!(),
322 };
323 assert_eq!(message, "Unknown attribute in Item element.".to_owned());
324 }
325
326 #[test]
327 fn test_item_affiliation_role_attr() {
328 let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#admin'
329 affiliation='member'
330 role='moderator'/>"
331 .parse()
332 .unwrap();
333 Item::try_from(elem).unwrap();
334 }
335
336 #[test]
337 fn test_item_affiliation_role_invalid_attr() {
338 let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#admin'
339 affiliation='member'/>"
340 .parse()
341 .unwrap();
342 let error = Item::try_from(elem).unwrap_err();
343 let message = match error {
344 FromElementError::Invalid(Error::Other(string)) => string,
345 _ => panic!(),
346 };
347 assert_eq!(
348 message,
349 "Required attribute field 'role' on Item element missing.".to_owned()
350 );
351 }
352
353 #[test]
354 fn test_item_nick_attr() {
355 let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#admin'
356 affiliation='member'
357 role='moderator'
358 nick='foobar'/>"
359 .parse()
360 .unwrap();
361 let item = Item::try_from(elem).unwrap();
362 match item {
363 Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())),
364 }
365 }
366
367 #[test]
368 fn test_item_affiliation_role_invalid_attr2() {
369 let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#admin'
370 role='moderator'/>"
371 .parse()
372 .unwrap();
373 let error = Item::try_from(elem).unwrap_err();
374 let message = match error {
375 FromElementError::Invalid(Error::Other(string)) => string,
376 _ => panic!(),
377 };
378 assert_eq!(
379 message,
380 "Required attribute field 'affiliation' on Item element missing.".to_owned()
381 );
382 }
383
384 #[test]
385 fn test_item_role_actor_child() {
386 let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#admin'
387 affiliation='member'
388 role='moderator'>
389 <actor nick='foobar'/>
390 </item>"
391 .parse()
392 .unwrap();
393 let item = Item::try_from(elem).unwrap();
394 let Item { actor, .. } = item;
395 let actor = actor.unwrap();
396 assert_eq!(actor.nick, Some("foobar".to_owned()));
397 assert_eq!(actor.jid, None);
398 }
399
400 #[test]
401 fn test_item_role_reason_child() {
402 let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#admin'
403 affiliation='member'
404 role='moderator'>
405 <reason>foobar</reason>
406 </item>"
407 .parse()
408 .unwrap();
409 let item = Item::try_from(elem).unwrap();
410 match item {
411 Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))),
412 }
413 }
414
415 #[test]
416 fn test_serialize_item() {
417 let reference: Element = "<item xmlns='http://jabber.org/protocol/muc#admin' affiliation='member' role='moderator'><actor nick='foobar'/><reason>foobar</reason></item>"
418 .parse()
419 .unwrap();
420
421 let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#admin' nick='foobar'/>"
422 .parse()
423 .unwrap();
424 let actor = Actor::try_from(elem).unwrap();
425
426 let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#admin'>foobar</reason>"
427 .parse()
428 .unwrap();
429 let reason = Reason::try_from(elem).unwrap();
430
431 let item = Item {
432 affiliation: Some(Affiliation::Member),
433 role: Some(Role::Moderator),
434 jid: None,
435 nick: None,
436 actor: Some(actor),
437 reason: Some(reason),
438 };
439
440 let serialized: Element = item.into();
441 println!("{reference:#?}");
442 println!("{serialized:#?}");
443 assert_eq!(serialized, reference);
444 }
445
446 #[test]
447 fn presence_payload() {
448 let elem: Element = "<query xmlns='http://jabber.org/protocol/muc#admin'/>"
449 .parse()
450 .unwrap();
451 let presence = Presence::new(PresenceType::None).with_payloads(vec![elem]);
452 assert_eq!(presence.payloads.len(), 1);
453 }
454
455 #[test]
456 fn message_payload() {
457 let jid: Jid = Jid::new("louise@example.com").unwrap();
458 let elem: Element = "<query xmlns='http://jabber.org/protocol/muc#admin'/>"
459 .parse()
460 .unwrap();
461 let message = Message::new(jid).with_payloads(vec![elem]);
462 assert_eq!(message.payloads.len(), 1);
463 }
464
465 #[test]
466 fn muc_item_affiliation_none() {
467 let elem: Element = "<query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='none' role='participant' /></query>"
468 .parse()
469 .unwrap();
470
471 let user = MucUser::try_from(elem).unwrap();
472 let item = user.items.iter().next().unwrap();
473 assert_eq!(item.affiliation, Some(Affiliation::None),);
474 assert_eq!(item.role, Some(Role::Participant),);
475
476 let item = Item::new()
477 .with_affiliation(Affiliation::None)
478 .with_role(Role::Participant);
479 let user = MucUser::new().with_items(vec![item]);
480 let elem: Element = user.into();
481 let item = elem.children().next().unwrap();
482 assert_eq!(item.attr("role").unwrap(), "participant");
483 assert_eq!(item.attr("affiliation").unwrap(), "none");
484 }
485
486 #[test]
487 #[ignore]
488 fn muc_item_no_affiliation() {
489 let elem: Element =
490 "<query xmlns='http://jabber.org/protocol/muc#admin'><item role='participant' /></x>"
491 .parse()
492 .unwrap();
493
494 let user = MucUser::try_from(elem).unwrap();
495 let item = user.items.iter().next().unwrap();
496 assert_eq!(item.affiliation, Some(Affiliation::None),);
497 assert_eq!(item.role, Some(Role::Participant),);
498
499 let item = Item::new()
500 .with_affiliation(Affiliation::None)
501 .with_role(Role::Participant);
502 let user = MucUser::new().with_items(vec![item]);
503 let elem: Element = user.into();
504 let item = elem.children().next().unwrap();
505 assert_eq!(item.attr("affiliation").unwrap(), "none");
506 }
507}