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<'a> TryFrom<&'a Element> for StanzaId {
28 type Error = Error;
29
30 fn try_from(elem: &'a 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 = match elem.attr("id") {
39 Some(id) => id.to_owned(),
40 None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")),
41 };
42 Ok(if is_stanza_id {
43 let by = match elem.attr("by") {
44 Some(by) => by.parse().unwrap(),
45 None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")),
46 };
47 StanzaId::StanzaId { id, by }
48 } else {
49 StanzaId::OriginId { id }
50 })
51 }
52}
53
54impl<'a> Into<Element> for &'a StanzaId {
55 fn into(self) -> Element {
56 match *self {
57 StanzaId::StanzaId { ref id, ref by } => {
58 Element::builder("stanza-id")
59 .ns(ns::SID)
60 .attr("id", id.clone())
61 .attr("by", String::from(by.clone()))
62 .build()
63 },
64 StanzaId::OriginId { ref id } => {
65 Element::builder("origin-id")
66 .ns(ns::SID)
67 .attr("id", id.clone())
68 .build()
69 },
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use std::str::FromStr;
78
79 #[test]
80 fn test_simple() {
81 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>".parse().unwrap();
82 let stanza_id = StanzaId::try_from(&elem).unwrap();
83 if let StanzaId::StanzaId { id, by } = stanza_id {
84 assert_eq!(id, String::from("coucou"));
85 assert_eq!(by, Jid::from_str("coucou@coucou").unwrap());
86 } else {
87 panic!();
88 }
89
90 let elem: Element = "<origin-id xmlns='urn:xmpp:sid:0' id='coucou'/>".parse().unwrap();
91 let stanza_id = StanzaId::try_from(&elem).unwrap();
92 if let StanzaId::OriginId { id } = stanza_id {
93 assert_eq!(id, String::from("coucou"));
94 } else {
95 panic!();
96 }
97 }
98
99 #[test]
100 fn test_invalid_child() {
101 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'><coucou/></stanza-id>".parse().unwrap();
102 let error = StanzaId::try_from(&elem).unwrap_err();
103 let message = match error {
104 Error::ParseError(string) => string,
105 _ => panic!(),
106 };
107 assert_eq!(message, "Unknown child in stanza-id or origin-id element.");
108 }
109
110 #[test]
111 fn test_invalid_id() {
112 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'/>".parse().unwrap();
113 let error = StanzaId::try_from(&elem).unwrap_err();
114 let message = match error {
115 Error::ParseError(string) => string,
116 _ => panic!(),
117 };
118 assert_eq!(message, "No 'id' attribute present in stanza-id or origin-id.");
119 }
120
121 #[test]
122 fn test_invalid_by() {
123 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou'/>".parse().unwrap();
124 let error = StanzaId::try_from(&elem).unwrap_err();
125 let message = match error {
126 Error::ParseError(string) => string,
127 _ => panic!(),
128 };
129 assert_eq!(message, "No 'by' attribute present in stanza-id.");
130 }
131
132 #[test]
133 fn test_serialise() {
134 let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>".parse().unwrap();
135 let stanza_id = StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() };
136 let elem2 = (&stanza_id).into();
137 assert_eq!(elem, elem2);
138 }
139}