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 std::convert::TryFrom;
8
9use minidom::Element;
10use jid::Jid;
11
12use error::Error;
13
14use ns;
15
16#[derive(Debug, Clone)]
17pub enum StanzaId {
18 StanzaId {
19 id: String,
20 by: Jid,
21 },
22 OriginId {
23 id: String,
24 },
25}
26
27impl TryFrom<Element> for StanzaId {
28 type Error = Error;
29
30 fn try_from(elem: Element) -> Result<StanzaId, Error> {
31 let is_stanza_id = elem.is("stanza-id", ns::SID);
32 if !is_stanza_id && !elem.is("origin-id", ns::SID) {
33 return Err(Error::ParseError("This is not a stanza-id or origin-id element."));
34 }
35 for _ in elem.children() {
36 return Err(Error::ParseError("Unknown child in stanza-id or origin-id element."));
37 }
38 let id = get_attr!(elem, "id", required);
39 Ok(if is_stanza_id {
40 let by = get_attr!(elem, "by", required);
41 StanzaId::StanzaId { id, by }
42 } else {
43 StanzaId::OriginId { id }
44 })
45 }
46}
47
48impl Into<Element> for StanzaId {
49 fn into(self) -> Element {
50 match self {
51 StanzaId::StanzaId { id, by } => {
52 Element::builder("stanza-id")
53 .ns(ns::SID)
54 .attr("id", id)
55 .attr("by", String::from(by))
56 .build()
57 },
58 StanzaId::OriginId { id } => {
59 Element::builder("origin-id")
60 .ns(ns::SID)
61 .attr("id", id)
62 .build()
63 },
64 }
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use std::str::FromStr;
72
73 #[test]
74 fn test_simple() {
75 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>".parse().unwrap();
76 let stanza_id = StanzaId::try_from(elem).unwrap();
77 if let StanzaId::StanzaId { id, by } = stanza_id {
78 assert_eq!(id, String::from("coucou"));
79 assert_eq!(by, Jid::from_str("coucou@coucou").unwrap());
80 } else {
81 panic!();
82 }
83
84 let elem: Element = "<origin-id xmlns='urn:xmpp:sid:0' id='coucou'/>".parse().unwrap();
85 let stanza_id = StanzaId::try_from(elem).unwrap();
86 if let StanzaId::OriginId { id } = stanza_id {
87 assert_eq!(id, String::from("coucou"));
88 } else {
89 panic!();
90 }
91 }
92
93 #[test]
94 fn test_invalid_child() {
95 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'><coucou/></stanza-id>".parse().unwrap();
96 let error = StanzaId::try_from(elem).unwrap_err();
97 let message = match error {
98 Error::ParseError(string) => string,
99 _ => panic!(),
100 };
101 assert_eq!(message, "Unknown child in stanza-id or origin-id element.");
102 }
103
104 #[test]
105 fn test_invalid_id() {
106 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'/>".parse().unwrap();
107 let error = StanzaId::try_from(elem).unwrap_err();
108 let message = match error {
109 Error::ParseError(string) => string,
110 _ => panic!(),
111 };
112 assert_eq!(message, "Required attribute 'id' missing.");
113 }
114
115 #[test]
116 fn test_invalid_by() {
117 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou'/>".parse().unwrap();
118 let error = StanzaId::try_from(elem).unwrap_err();
119 let message = match error {
120 Error::ParseError(string) => string,
121 _ => panic!(),
122 };
123 assert_eq!(message, "Required attribute 'by' missing.");
124 }
125
126 #[test]
127 fn test_serialise() {
128 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>".parse().unwrap();
129 let stanza_id = StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() };
130 let elem2 = stanza_id.into();
131 assert_eq!(elem, elem2);
132 }
133}