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