data_forms.rs

  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}