1extern crate minidom;
2
3use minidom::Element;
4
5use error::Error;
6use ns::{DISCO_INFO_NS, DATA_FORMS_NS};
7
8use data_forms::{DataForm, DataFormType, parse_data_form};
9
10#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
11pub struct Feature {
12 pub var: String,
13}
14
15#[derive(Debug)]
16pub struct Identity {
17 pub category: String, // TODO: use an enum here.
18 pub type_: String, // TODO: use an enum here.
19 pub xml_lang: String,
20 pub name: Option<String>,
21}
22
23#[derive(Debug)]
24pub struct Disco {
25 pub node: Option<String>,
26 pub identities: Vec<Identity>,
27 pub features: Vec<Feature>,
28 pub extensions: Vec<DataForm>,
29}
30
31pub fn parse_disco(root: &Element) -> Result<Disco, Error> {
32 assert!(root.is("query", DISCO_INFO_NS));
33 let mut identities: Vec<Identity> = vec!();
34 let mut features: Vec<Feature> = vec!();
35 let mut extensions: Vec<DataForm> = vec!();
36
37 let node = root.attr("node")
38 .and_then(|node| node.parse().ok());
39
40 for child in root.children() {
41 if child.is("feature", DISCO_INFO_NS) {
42 let feature = child.attr("var")
43 .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?;
44 features.push(Feature {
45 var: feature.to_owned(),
46 });
47 } else if child.is("identity", DISCO_INFO_NS) {
48 let category = child.attr("category")
49 .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?;
50 if category == "" {
51 return Err(Error::ParseError("Identity must have a non-empty 'category' attribute."))
52 }
53
54 let type_ = child.attr("type")
55 .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?;
56 if type_ == "" {
57 return Err(Error::ParseError("Identity must have a non-empty 'type' attribute."))
58 }
59
60 // TODO: this must check for the namespace of the attribute, but minidom doesn’t support that yet, see issue #2.
61 let xml_lang = child.attr("lang").unwrap_or("");
62 let name = child.attr("name")
63 .and_then(|name| name.parse().ok());
64 identities.push(Identity {
65 category: category.to_owned(),
66 type_: type_.to_owned(),
67 xml_lang: xml_lang.to_owned(),
68 name: name,
69 });
70 } else if child.is("x", DATA_FORMS_NS) {
71 let data_form = parse_data_form(child)?;
72 match data_form.type_ {
73 DataFormType::Result_ => (),
74 _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")),
75 }
76 match data_form.form_type {
77 Some(_) => extensions.push(data_form),
78 None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")),
79 }
80 } else {
81 return Err(Error::ParseError("Unknown element in disco#info."));
82 }
83 }
84
85 if identities.is_empty() {
86 return Err(Error::ParseError("There must be at least one identity in disco#info."));
87 }
88 if features.is_empty() {
89 return Err(Error::ParseError("There must be at least one feature in disco#info."));
90 }
91 if !features.contains(&Feature { var: DISCO_INFO_NS.to_owned() }) {
92 return Err(Error::ParseError("disco#info feature not present in disco#info."));
93 }
94
95 return Ok(Disco {
96 node: node,
97 identities: identities,
98 features: features,
99 extensions: extensions
100 });
101}
102
103#[cfg(test)]
104mod tests {
105 use minidom::Element;
106 use error::Error;
107 use disco;
108
109 #[test]
110 fn test_simple() {
111 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/><feature var='http://jabber.org/protocol/disco#info'/></query>".parse().unwrap();
112 let query = disco::parse_disco(&elem).unwrap();
113 assert!(query.node.is_none());
114 assert_eq!(query.identities.len(), 1);
115 assert_eq!(query.features.len(), 1);
116 assert!(query.extensions.is_empty());
117 }
118
119 #[test]
120 fn test_invalid() {
121 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><coucou/></query>".parse().unwrap();
122 let error = disco::parse_disco(&elem).unwrap_err();
123 let message = match error {
124 Error::ParseError(string) => string,
125 _ => panic!(),
126 };
127 assert_eq!(message, "Unknown element in disco#info.");
128
129 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'/>".parse().unwrap();
130 let error = disco::parse_disco(&elem).unwrap_err();
131 let message = match error {
132 Error::ParseError(string) => string,
133 _ => panic!(),
134 };
135 assert_eq!(message, "There must be at least one identity in disco#info.");
136
137 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity/></query>".parse().unwrap();
138 let error = disco::parse_disco(&elem).unwrap_err();
139 let message = match error {
140 Error::ParseError(string) => string,
141 _ => panic!(),
142 };
143 assert_eq!(message, "Identity must have a 'category' attribute.");
144
145 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category=''/></query>".parse().unwrap();
146 let error = disco::parse_disco(&elem).unwrap_err();
147 let message = match error {
148 Error::ParseError(string) => string,
149 _ => panic!(),
150 };
151 assert_eq!(message, "Identity must have a non-empty 'category' attribute.");
152
153 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou'/></query>".parse().unwrap();
154 let error = disco::parse_disco(&elem).unwrap_err();
155 let message = match error {
156 Error::ParseError(string) => string,
157 _ => panic!(),
158 };
159 assert_eq!(message, "Identity must have a 'type' attribute.");
160
161 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou' type=''/></query>".parse().unwrap();
162 let error = disco::parse_disco(&elem).unwrap_err();
163 let message = match error {
164 Error::ParseError(string) => string,
165 _ => panic!(),
166 };
167 assert_eq!(message, "Identity must have a non-empty 'type' attribute.");
168
169 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><feature/></query>".parse().unwrap();
170 let error = disco::parse_disco(&elem).unwrap_err();
171 let message = match error {
172 Error::ParseError(string) => string,
173 _ => panic!(),
174 };
175 assert_eq!(message, "Feature must have a 'var' attribute.");
176
177 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/></query>".parse().unwrap();
178 let error = disco::parse_disco(&elem).unwrap_err();
179 let message = match error {
180 Error::ParseError(string) => string,
181 _ => panic!(),
182 };
183 assert_eq!(message, "There must be at least one feature in disco#info.");
184
185 let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/><feature var='http://jabber.org/protocol/disco#items'/></query>".parse().unwrap();
186 let error = disco::parse_disco(&elem).unwrap_err();
187 let message = match error {
188 Error::ParseError(string) => string,
189 _ => panic!(),
190 };
191 assert_eq!(message, "disco#info feature not present in disco#info.");
192 }
193}