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