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
  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}