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;
13use ns;
14
15use media_element::MediaElement;
16
17#[derive(Debug, Clone, PartialEq)]
18pub enum FieldType {
19 Boolean,
20 Fixed,
21 Hidden,
22 JidMulti,
23 JidSingle,
24 ListMulti,
25 ListSingle,
26 TextMulti,
27 TextPrivate,
28 TextSingle,
29}
30
31impl Default for FieldType {
32 fn default() -> FieldType {
33 FieldType::TextSingle
34 }
35}
36
37impl FromStr for FieldType {
38 type Err = Error;
39
40 fn from_str(s: &str) -> Result<FieldType, Error> {
41 Ok(match s {
42 "boolean" => FieldType::Boolean,
43 "fixed" => FieldType::Fixed,
44 "hidden" => FieldType::Hidden,
45 "jid-multi" => FieldType::JidMulti,
46 "jid-single" => FieldType::JidSingle,
47 "list-multi" => FieldType::ListMulti,
48 "list-single" => FieldType::ListSingle,
49 "text-multi" => FieldType::TextMulti,
50 "text-private" => FieldType::TextPrivate,
51 "text-single" => FieldType::TextSingle,
52
53 _ => return Err(Error::ParseError("Invalid 'type' attribute in field element.")),
54 })
55 }
56}
57
58#[derive(Debug, Clone)]
59pub struct Option_ {
60 pub label: Option<String>,
61 pub value: String,
62}
63
64#[derive(Debug, Clone)]
65pub struct Field {
66 pub var: String,
67 pub type_: FieldType,
68 pub label: Option<String>,
69 pub required: bool,
70 pub options: Vec<Option_>,
71 pub values: Vec<String>,
72 pub media: Vec<MediaElement>,
73}
74
75#[derive(Debug, Clone, PartialEq)]
76pub enum DataFormType {
77 Cancel,
78 Form,
79 Result_,
80 Submit,
81}
82
83impl FromStr for DataFormType {
84 type Err = Error;
85
86 fn from_str(s: &str) -> Result<DataFormType, Error> {
87 Ok(match s {
88 "cancel" => DataFormType::Cancel,
89 "form" => DataFormType::Form,
90 "result" => DataFormType::Result_,
91 "submit" => DataFormType::Submit,
92
93 _ => return Err(Error::ParseError("Unknown data form type.")),
94 })
95 }
96}
97
98#[derive(Debug, Clone)]
99pub struct DataForm {
100 pub type_: DataFormType,
101 pub form_type: Option<String>,
102 pub title: Option<String>,
103 pub instructions: Option<String>,
104 pub fields: Vec<Field>,
105}
106
107impl TryFrom<Element> for DataForm {
108 type Error = Error;
109
110 fn try_from(elem: Element) -> Result<DataForm, Error> {
111 if !elem.is("x", ns::DATA_FORMS) {
112 return Err(Error::ParseError("This is not a data form element."));
113 }
114 let type_ = get_attr!(elem, "type", required);
115 let mut form = DataForm {
116 type_: type_,
117 form_type: None,
118 title: None,
119 instructions: None,
120 fields: vec!(),
121 };
122 for child in elem.children() {
123 if child.is("title", ns::DATA_FORMS) {
124 if form.title.is_some() {
125 return Err(Error::ParseError("More than one title in form element."));
126 }
127 for _ in child.children() {
128 return Err(Error::ParseError("Title element must not have any child."));
129 }
130 for _ in child.attrs() {
131 return Err(Error::ParseError("Title element must not have any attribute."));
132 }
133 form.title = Some(child.text());
134 } else if child.is("instructions", ns::DATA_FORMS) {
135 if form.instructions.is_some() {
136 return Err(Error::ParseError("More than one instructions in form element."));
137 }
138 for _ in child.children() {
139 return Err(Error::ParseError("instructions element must not have any child."));
140 }
141 for _ in child.attrs() {
142 return Err(Error::ParseError("instructions element must not have any attribute."));
143 }
144 form.instructions = Some(child.text());
145 } else if child.is("field", ns::DATA_FORMS) {
146 let var: String = get_attr!(child, "var", required);
147 let field_type = get_attr!(child, "type", default);
148 let label = get_attr!(child, "label", optional);
149
150 let is_form_type = var == "FORM_TYPE" && field_type == FieldType::Hidden;
151 let is_list = field_type == FieldType::ListSingle || field_type == FieldType::ListMulti;
152 let mut field = Field {
153 var: var,
154 type_: field_type,
155 label: label,
156 required: false,
157 options: vec!(),
158 values: vec!(),
159 media: vec!(),
160 };
161 for element in child.children() {
162 if element.is("value", ns::DATA_FORMS) {
163 for _ in element.children() {
164 return Err(Error::ParseError("Value element must not have any child."));
165 }
166 for _ in element.attrs() {
167 return Err(Error::ParseError("Value element must not have any attribute."));
168 }
169 field.values.push(element.text());
170 } else if element.is("required", ns::DATA_FORMS) {
171 if field.required {
172 return Err(Error::ParseError("More than one required element."));
173 }
174 for _ in element.children() {
175 return Err(Error::ParseError("Required element must not have any child."));
176 }
177 for _ in element.attrs() {
178 return Err(Error::ParseError("Required element must not have any attribute."));
179 }
180 field.required = true;
181 } else if element.is("option", ns::DATA_FORMS) {
182 if !is_list {
183 return Err(Error::ParseError("Option element found in non-list field."));
184 }
185 let label = get_attr!(element, "label", optional);
186 let mut value = None;
187 for child2 in element.children() {
188 if child2.is("value", ns::DATA_FORMS) {
189 if value.is_some() {
190 return Err(Error::ParseError("More than one value element in option element"));
191 }
192 value = Some(child2.text());
193 } else {
194 return Err(Error::ParseError("Non-value element in option element"));
195 }
196 }
197 let value = value.ok_or(Error::ParseError("No value element in option element"))?;
198 field.options.push(Option_ {
199 label: label,
200 value: value,
201 });
202 } else if element.is("media", ns::MEDIA_ELEMENT) {
203 match MediaElement::try_from(element.clone()) {
204 Ok(media_element) => field.media.push(media_element),
205 Err(_) => (), // TODO: is it really nice to swallow this error?
206 }
207 } else {
208 return Err(Error::ParseError("Field child isn’t a value or media element."));
209 }
210 }
211 if is_form_type {
212 if form.form_type.is_some() {
213 return Err(Error::ParseError("More than one FORM_TYPE in a data form."));
214 }
215 if field.values.len() != 1 {
216 return Err(Error::ParseError("Wrong number of values in FORM_TYPE."));
217 }
218 form.form_type = Some(field.values[0].clone());
219 }
220 form.fields.push(field);
221 } else {
222 return Err(Error::ParseError("Unknown child in data form element."));
223 }
224 }
225 Ok(form)
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[test]
234 fn test_simple() {
235 let elem: Element = "<x xmlns='jabber:x:data' type='result'/>".parse().unwrap();
236 let form = DataForm::try_from(elem).unwrap();
237 assert_eq!(form.type_, DataFormType::Result_);
238 assert!(form.form_type.is_none());
239 assert!(form.fields.is_empty());
240 }
241
242 #[test]
243 fn test_invalid() {
244 let elem: Element = "<x xmlns='jabber:x:data'/>".parse().unwrap();
245 let error = DataForm::try_from(elem).unwrap_err();
246 let message = match error {
247 Error::ParseError(string) => string,
248 _ => panic!(),
249 };
250 assert_eq!(message, "Required attribute 'type' missing.");
251
252 let elem: Element = "<x xmlns='jabber:x:data' type='coucou'/>".parse().unwrap();
253 let error = DataForm::try_from(elem).unwrap_err();
254 let message = match error {
255 Error::ParseError(string) => string,
256 _ => panic!(),
257 };
258 assert_eq!(message, "Unknown data form type.");
259 }
260
261 #[test]
262 fn test_wrong_child() {
263 let elem: Element = "<x xmlns='jabber:x:data' type='cancel'><coucou/></x>".parse().unwrap();
264 let error = DataForm::try_from(elem).unwrap_err();
265 let message = match error {
266 Error::ParseError(string) => string,
267 _ => panic!(),
268 };
269 assert_eq!(message, "Unknown child in data form element.");
270 }
271}