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;
8use std::str::FromStr;
9
10use minidom::Element;
11
12use error::Error;
13
14use ns;
15
16use ibb::Stanza;
17
18#[derive(Debug, Clone)]
19pub struct Transport {
20 pub block_size: u16,
21 pub sid: String,
22 pub stanza: Stanza,
23}
24
25fn optional_attr<T: FromStr>(root: &Element, attr: &str) -> Option<T> {
26 root.attr(attr)
27 .and_then(|value| value.parse().ok())
28}
29
30fn required_attr<T: FromStr>(root: &Element, attr: &str, err: Error) -> Result<T, Error> {
31 optional_attr(root, attr).ok_or(err)
32}
33
34impl<'a> TryFrom<&'a Element> for Transport {
35 type Error = Error;
36
37 fn try_from(elem: &'a Element) -> Result<Transport, Error> {
38 if elem.is("transport", ns::JINGLE_IBB) {
39 for _ in elem.children() {
40 return Err(Error::ParseError("Unknown child in JingleIBB element."));
41 }
42 let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?;
43 let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?;
44 let stanza = elem.attr("stanza")
45 .unwrap_or("iq")
46 .parse()?;
47 Ok(Transport {
48 block_size: block_size,
49 sid: sid,
50 stanza: stanza
51 })
52 } else {
53 Err(Error::ParseError("This is not an JingleIBB element."))
54 }
55 }
56}
57
58impl<'a> Into<Element> for &'a Transport {
59 fn into(self) -> Element {
60 Element::builder("transport")
61 .ns(ns::JINGLE_IBB)
62 .attr("block-size", format!("{}", self.block_size))
63 .attr("sid", self.sid.clone())
64 .attr("stanza", self.stanza.clone())
65 .build()
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_simple() {
75 let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='3' sid='coucou'/>".parse().unwrap();
76 let transport = Transport::try_from(&elem).unwrap();
77 assert_eq!(transport.block_size, 3);
78 assert_eq!(transport.sid, "coucou");
79 assert_eq!(transport.stanza, Stanza::Iq);
80 }
81
82 #[test]
83 fn test_invalid() {
84 let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1'/>".parse().unwrap();
85 let error = Transport::try_from(&elem).unwrap_err();
86 let message = match error {
87 Error::ParseError(string) => string,
88 _ => panic!(),
89 };
90 assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element.");
91
92 // TODO: maybe make a better error message here.
93 let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='-5'/>".parse().unwrap();
94 let error = Transport::try_from(&elem).unwrap_err();
95 let message = match error {
96 Error::ParseError(string) => string,
97 _ => panic!(),
98 };
99 assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element.");
100
101 let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='128'/>".parse().unwrap();
102 let error = Transport::try_from(&elem).unwrap_err();
103 let message = match error {
104 Error::ParseError(string) => string,
105 _ => panic!(),
106 };
107 assert_eq!(message, "Required attribute 'sid' missing in JingleIBB element.");
108 }
109
110 #[test]
111 fn test_invalid_stanza() {
112 let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
113 let error = Transport::try_from(&elem).unwrap_err();
114 let message = match error {
115 Error::ParseError(string) => string,
116 _ => panic!(),
117 };
118 assert_eq!(message, "Invalid 'stanza' attribute.");
119 }
120}