1use crate::ns;
9use crate::stanza_error::StanzaError;
10use jid::Jid;
11use minidom::Element;
12use xso::{AsXml, FromXml};
13
14pub trait IqGetPayload: TryFrom<Element> + Into<Element> {}
16
17pub trait IqSetPayload: TryFrom<Element> + Into<Element> {}
19
20pub trait IqResultPayload: TryFrom<Element> + Into<Element> {}
22
23pub struct IqHeader {
25 pub from: Option<Jid>,
27
28 pub to: Option<Jid>,
30
31 pub id: String,
33}
34
35impl IqHeader {
36 pub fn assemble(self, data: IqPayload) -> Iq {
38 data.assemble(self)
39 }
40}
41
42pub enum IqPayload {
44 Get(Element),
46
47 Set(Element),
49
50 Result(Option<Element>),
52
53 Error(StanzaError),
55}
56
57impl IqPayload {
58 pub fn assemble(self, IqHeader { from, to, id }: IqHeader) -> Iq {
60 match self {
61 Self::Get(payload) => Iq::Get {
62 from,
63 to,
64 id,
65 payload,
66 },
67 Self::Set(payload) => Iq::Set {
68 from,
69 to,
70 id,
71 payload,
72 },
73 Self::Result(payload) => Iq::Result {
74 from,
75 to,
76 id,
77 payload,
78 },
79 Self::Error(error) => Iq::Error {
80 from,
81 to,
82 id,
83 payload: None,
84 error,
85 },
86 }
87 }
88}
89
90#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
92#[xml(namespace = ns::DEFAULT_NS, name = "iq", attribute = "type", exhaustive)]
93pub enum Iq {
94 #[xml(value = "get")]
96 Get {
97 #[xml(attribute(default))]
99 from: Option<Jid>,
100
101 #[xml(attribute(default))]
103 to: Option<Jid>,
104
105 #[xml(attribute)]
108 id: String,
109
110 #[xml(element(n = 1))]
112 payload: Element,
113 },
114
115 #[xml(value = "set")]
117 Set {
118 #[xml(attribute(default))]
120 from: Option<Jid>,
121
122 #[xml(attribute(default))]
124 to: Option<Jid>,
125
126 #[xml(attribute)]
129 id: String,
130
131 #[xml(element(n = 1))]
133 payload: Element,
134 },
135
136 #[xml(value = "result")]
138 Result {
139 #[xml(attribute(default))]
141 from: Option<Jid>,
142
143 #[xml(attribute(default))]
145 to: Option<Jid>,
146
147 #[xml(attribute)]
150 id: String,
151
152 #[xml(element(n = 1, default))]
154 payload: Option<Element>,
155 },
156
157 #[xml(value = "error")]
159 Error {
160 #[xml(attribute(default))]
162 from: Option<Jid>,
163
164 #[xml(attribute(default))]
166 to: Option<Jid>,
167
168 #[xml(attribute)]
171 id: String,
172
173 #[xml(child)]
175 error: StanzaError,
176
177 #[xml(element(n = 1, default))]
184 payload: Option<Element>,
185 },
186}
187
188impl Iq {
189 pub fn assemble(header: IqHeader, data: IqPayload) -> Self {
192 data.assemble(header)
193 }
194
195 pub fn from_get<S: Into<String>>(id: S, payload: impl IqGetPayload) -> Iq {
197 Iq::Get {
198 from: None,
199 to: None,
200 id: id.into(),
201 payload: payload.into(),
202 }
203 }
204
205 pub fn from_set<S: Into<String>>(id: S, payload: impl IqSetPayload) -> Iq {
207 Iq::Set {
208 from: None,
209 to: None,
210 id: id.into(),
211 payload: payload.into(),
212 }
213 }
214
215 pub fn empty_result<S: Into<String>>(to: Jid, id: S) -> Iq {
217 Iq::Result {
218 from: None,
219 to: Some(to),
220 id: id.into(),
221 payload: None,
222 }
223 }
224
225 pub fn from_result<S: Into<String>>(id: S, payload: Option<impl IqResultPayload>) -> Iq {
227 Iq::Result {
228 from: None,
229 to: None,
230 id: id.into(),
231 payload: payload.map(Into::into),
232 }
233 }
234
235 pub fn from_error<S: Into<String>>(id: S, payload: StanzaError) -> Iq {
237 Iq::Error {
238 from: None,
239 to: None,
240 id: id.into(),
241 error: payload,
242 payload: None,
243 }
244 }
245
246 pub fn with_to(mut self, to: Jid) -> Iq {
248 *self.to_mut() = Some(to);
249 self
250 }
251
252 pub fn with_from(mut self, from: Jid) -> Iq {
254 *self.from_mut() = Some(from);
255 self
256 }
257
258 pub fn with_id(mut self, id: String) -> Iq {
260 *self.id_mut() = id;
261 self
262 }
263
264 pub fn from(&self) -> Option<&Jid> {
266 match self {
267 Self::Get { from, .. }
268 | Self::Set { from, .. }
269 | Self::Result { from, .. }
270 | Self::Error { from, .. } => from.as_ref(),
271 }
272 }
273
274 pub fn from_mut(&mut self) -> &mut Option<Jid> {
276 match self {
277 Self::Get { ref mut from, .. }
278 | Self::Set { ref mut from, .. }
279 | Self::Result { ref mut from, .. }
280 | Self::Error { ref mut from, .. } => from,
281 }
282 }
283
284 pub fn to(&self) -> Option<&Jid> {
286 match self {
287 Self::Get { to, .. }
288 | Self::Set { to, .. }
289 | Self::Result { to, .. }
290 | Self::Error { to, .. } => to.as_ref(),
291 }
292 }
293
294 pub fn to_mut(&mut self) -> &mut Option<Jid> {
296 match self {
297 Self::Get { ref mut to, .. }
298 | Self::Set { ref mut to, .. }
299 | Self::Result { ref mut to, .. }
300 | Self::Error { ref mut to, .. } => to,
301 }
302 }
303
304 pub fn id(&self) -> &str {
306 match self {
307 Self::Get { id, .. }
308 | Self::Set { id, .. }
309 | Self::Result { id, .. }
310 | Self::Error { id, .. } => id.as_str(),
311 }
312 }
313
314 pub fn id_mut(&mut self) -> &mut String {
316 match self {
317 Self::Get { ref mut id, .. }
318 | Self::Set { ref mut id, .. }
319 | Self::Result { ref mut id, .. }
320 | Self::Error { ref mut id, .. } => id,
321 }
322 }
323
324 pub fn split(self) -> (IqHeader, IqPayload) {
330 match self {
331 Self::Get {
332 from,
333 to,
334 id,
335 payload,
336 } => (IqHeader { from, to, id }, IqPayload::Get(payload)),
337 Self::Set {
338 from,
339 to,
340 id,
341 payload,
342 } => (IqHeader { from, to, id }, IqPayload::Set(payload)),
343 Self::Result {
344 from,
345 to,
346 id,
347 payload,
348 } => (IqHeader { from, to, id }, IqPayload::Result(payload)),
349 Self::Error {
350 from,
351 to,
352 id,
353 error,
354 payload: _,
355 } => (IqHeader { from, to, id }, IqPayload::Error(error)),
356 }
357 }
358
359 pub fn into_header(self) -> IqHeader {
361 self.split().0
362 }
363
364 pub fn into_payload(self) -> IqPayload {
370 self.split().1
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377 use crate::disco::DiscoInfoQuery;
378 use crate::stanza_error::{DefinedCondition, ErrorType};
379 use xso::error::{Error, FromElementError};
380
381 #[cfg(target_pointer_width = "32")]
382 #[test]
383 fn test_size() {
384 assert_size!(IqHeader, 44);
385 assert_size!(IqPayload, 108);
386 assert_size!(Iq, 212);
387 }
388
389 #[cfg(target_pointer_width = "64")]
390 #[test]
391 fn test_size() {
392 assert_size!(IqHeader, 88);
393 assert_size!(IqPayload, 216);
394 assert_size!(Iq, 424);
395 }
396
397 #[test]
398 fn test_require_type() {
399 #[cfg(not(feature = "component"))]
400 let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
401 #[cfg(feature = "component")]
402 let elem: Element = "<iq xmlns='jabber:component:accept'/>".parse().unwrap();
403 let error = Iq::try_from(elem).unwrap_err();
404 let message = match error {
405 FromElementError::Invalid(Error::Other(string)) => string,
406 _ => panic!(),
407 };
408 assert_eq!(message, "Missing discriminator attribute.");
409 }
410
411 #[test]
412 fn test_require_id() {
413 for type_ in ["get", "set", "result", "error"] {
414 #[cfg(not(feature = "component"))]
415 let elem: Element = format!("<iq xmlns='jabber:client' type='{}'/>", type_)
416 .parse()
417 .unwrap();
418 #[cfg(feature = "component")]
419 let elem: Element = format!("<iq xmlns='jabber:component:accept' type='{}'/>", type_)
420 .parse()
421 .unwrap();
422 let error = Iq::try_from(elem).unwrap_err();
423 let message = match error {
424 FromElementError::Invalid(Error::Other(string)) => string,
425 _ => panic!(),
426 };
427 assert_eq!(&message[..33], "Required attribute field 'id' on ");
430 }
431 }
432
433 #[test]
434 fn test_get() {
435 #[cfg(not(feature = "component"))]
436 let elem: Element = "<iq xmlns='jabber:client' type='get' id='foo'>
437 <foo xmlns='bar'/>
438 </iq>"
439 .parse()
440 .unwrap();
441 #[cfg(feature = "component")]
442 let elem: Element = "<iq xmlns='jabber:component:accept' type='get' id='foo'>
443 <foo xmlns='bar'/>
444 </iq>"
445 .parse()
446 .unwrap();
447 let iq = Iq::try_from(elem).unwrap();
448 let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
449 assert_eq!(iq.from(), None);
450 assert_eq!(iq.to(), None);
451 assert_eq!(iq.id(), "foo");
452 assert!(match iq {
453 Iq::Get { payload, .. } => payload == query,
454 _ => false,
455 });
456 }
457
458 #[test]
459 fn test_set() {
460 #[cfg(not(feature = "component"))]
461 let elem: Element = "<iq xmlns='jabber:client' type='set' id='vcard'>
462 <vCard xmlns='vcard-temp'/>
463 </iq>"
464 .parse()
465 .unwrap();
466 #[cfg(feature = "component")]
467 let elem: Element = "<iq xmlns='jabber:component:accept' type='set' id='vcard'>
468 <vCard xmlns='vcard-temp'/>
469 </iq>"
470 .parse()
471 .unwrap();
472 let iq = Iq::try_from(elem).unwrap();
473 let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
474 assert_eq!(iq.from(), None);
475 assert_eq!(iq.to(), None);
476 assert_eq!(iq.id(), "vcard");
477 assert!(match iq {
478 Iq::Set { payload, .. } => payload == vcard,
479 _ => false,
480 });
481 }
482
483 #[test]
484 fn test_result_empty() {
485 #[cfg(not(feature = "component"))]
486 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'/>"
487 .parse()
488 .unwrap();
489 #[cfg(feature = "component")]
490 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'/>"
491 .parse()
492 .unwrap();
493 let iq = Iq::try_from(elem).unwrap();
494 assert_eq!(iq.from(), None);
495 assert_eq!(iq.to(), None);
496 assert_eq!(iq.id(), "res");
497 assert!(match iq {
498 Iq::Result { payload: None, .. } => true,
499 _ => false,
500 });
501 }
502
503 #[test]
504 fn test_result() {
505 #[cfg(not(feature = "component"))]
506 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'>
507 <query xmlns='http://jabber.org/protocol/disco#items'/>
508 </iq>"
509 .parse()
510 .unwrap();
511 #[cfg(feature = "component")]
512 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'>
513 <query xmlns='http://jabber.org/protocol/disco#items'/>
514 </iq>"
515 .parse()
516 .unwrap();
517 let iq = Iq::try_from(elem).unwrap();
518 let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
519 .parse()
520 .unwrap();
521 assert_eq!(iq.from(), None);
522 assert_eq!(iq.to(), None);
523 assert_eq!(iq.id(), "res");
524 assert!(match iq {
525 Iq::Result {
526 payload: Some(element),
527 ..
528 } => element == query,
529 _ => false,
530 });
531 }
532
533 #[test]
534 fn test_error() {
535 #[cfg(not(feature = "component"))]
536 let elem: Element = "<iq xmlns='jabber:client' type='error' id='err1'>
537 <ping xmlns='urn:xmpp:ping'/>
538 <error type='cancel'>
539 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
540 </error>
541 </iq>"
542 .parse()
543 .unwrap();
544 #[cfg(feature = "component")]
545 let elem: Element = "<iq xmlns='jabber:component:accept' type='error' id='err1'>
546 <ping xmlns='urn:xmpp:ping'/>
547 <error type='cancel'>
548 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
549 </error>
550 </iq>"
551 .parse()
552 .unwrap();
553 let iq = Iq::try_from(elem).unwrap();
554 assert_eq!(iq.from(), None);
555 assert_eq!(iq.to(), None);
556 assert_eq!(iq.id(), "err1");
557 match iq {
558 Iq::Error { error, .. } => {
559 assert_eq!(error.type_, ErrorType::Cancel);
560 assert_eq!(error.by, None);
561 assert_eq!(
562 error.defined_condition,
563 DefinedCondition::ServiceUnavailable
564 );
565 assert_eq!(error.texts.len(), 0);
566 assert_eq!(error.other, None);
567 }
568 _ => panic!(),
569 }
570 }
571
572 #[test]
573 fn test_children_invalid() {
574 #[cfg(not(feature = "component"))]
575 let elem: Element = "<iq xmlns='jabber:client' type='error' id='error'/>"
576 .parse()
577 .unwrap();
578 #[cfg(feature = "component")]
579 let elem: Element = "<iq xmlns='jabber:component:accept' type='error' id='error'/>"
580 .parse()
581 .unwrap();
582 let error = Iq::try_from(elem).unwrap_err();
583 let message = match error {
584 FromElementError::Invalid(Error::Other(string)) => string,
585 _ => panic!(),
586 };
587 assert_eq!(message, "Missing child field 'error' in Iq::Error element.");
588 }
589
590 #[test]
591 fn test_serialise() {
592 #[cfg(not(feature = "component"))]
593 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'/>"
594 .parse()
595 .unwrap();
596 #[cfg(feature = "component")]
597 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'/>"
598 .parse()
599 .unwrap();
600 let iq2 = Iq::Result {
601 from: None,
602 to: None,
603 id: String::from("res"),
604 payload: None,
605 };
606 let elem2 = iq2.into();
607 assert_eq!(elem, elem2);
608 }
609
610 #[test]
611 fn test_disco() {
612 #[cfg(not(feature = "component"))]
613 let elem: Element = "<iq xmlns='jabber:client' type='get' id='disco'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
614 #[cfg(feature = "component")]
615 let elem: Element = "<iq xmlns='jabber:component:accept' type='get' id='disco'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
616 let iq = Iq::try_from(elem).unwrap();
617 let disco_info = match iq {
618 Iq::Get { payload, .. } => DiscoInfoQuery::try_from(payload).unwrap(),
619 _ => panic!(),
620 };
621 assert!(disco_info.node.is_none());
622 }
623}