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