es_htmlform/
htmlform.rs

1use std::str::FromStr;
2use std::collections::HashMap;
3
4use serde::ser::{Serialize, Serializer, SerializeStruct};
5
6use crate::error::{FormError, ValidationError};
7use crate::value::{ValueMap, Value};
8use crate::types::{
9    Method, Element, InputType, SelectType, ButtonType, Constraint, Attr};
10
11/// `HtmlForm` represents an HTML form. It is used to validate data (both on
12/// the server and the client side) and to serialize forms in a consistent
13/// manner (either as JSON or using a template language of choice). The
14/// builder-style API makes it relatively easy to define forms:
15///
16/// ```rust
17/// use es_htmlform::HtmlForm;
18/// use es_htmlform::value::ValueMap;
19/// use es_htmlform::types::{Method, InputType, Constraint, Attr};
20///
21/// fn main() {
22///     // user input
23///     let values = ValueMap::from_urlencoded(b"foo=bar").unwrap();
24///     let mut form = HtmlForm::new(".", Method::Post)
25///         .input(
26///             InputType::Text, "foo", "Foo", true,
27///             vec![], vec![]).unwrap()
28///         .submit(None, "Submit", vec![]).unwrap();
29///     form.update(&values, true);
30///     assert_eq!(form.errors.len(), 0);
31///     assert_eq!(form.get_string("foo").unwrap(), "bar");
32/// }
33/// ```
34#[derive(Debug)]
35pub struct HtmlForm<'a> {
36    pub action: &'a str,
37    pub method: Method,
38    pub errors: HashMap<String, String>,
39    pub fields: Vec<Field<'a>>,
40}
41
42impl <'a> HtmlForm<'a> {
43    /// Instantiate an HtmlForm.
44    pub fn new(action: &'a str, method: Method) -> HtmlForm<'a> {
45        HtmlForm {
46            action,
47            method,
48            errors: HashMap::new(),
49            fields: Vec::new(),
50        }
51    }
52
53    /// Validate the values in a `ValueMap` and save them on the form
54    ///
55    /// This populates self.errors and the values in self.fields, the latter
56    /// regardless of whether the values are valid (this to allow presenting
57    /// a form with errors with the old values pre-filled - note that
58    /// password fields are emptied on serialization).
59    ///
60    /// Example:
61    ///
62    /// ```rust
63    /// use es_htmlform::HtmlForm;
64    /// use es_htmlform::value::ValueMap;
65    /// use es_htmlform::types::{Method, InputType, Constraint};
66    ///
67    /// fn main() {
68    ///     let mut form = HtmlForm::new(".", Method::Post)
69    ///         .input(
70    ///             InputType::Text, "foo", "Foo", true,
71    ///             vec![Constraint::MinLength(5)], vec![]).unwrap();
72    ///     form.update(
73    ///         &ValueMap::from_urlencoded(b"foo=bar").unwrap(), true);
74    ///     assert_eq!(
75    ///         form.errors.get("foo").unwrap(),
76    ///         "Must be at least 5 characters long.");
77    ///     assert_eq!(form.get::<String>("foo").unwrap(), vec!["bar"]);
78    /// }
79    /// ```
80    pub fn update(&mut self, values: &ValueMap, check_required: bool) {
81        self.errors.drain();
82        for field in &mut self.fields {
83            if let Some(values) = values.values(&field.name) {
84                if !field.element.multi() && values.len() > 1 {
85                    self.errors.insert(
86                        field.name.to_string(),
87                        String::from("field can only have one value"));
88                } else {
89                    let values: Vec<&Value> = values.iter()
90                        .map(|v| v)
91                        .collect();
92                    if let Err(e) = field.validate(&values) {
93                        self.errors.insert(
94                            field.name.to_string(), format!("{}", e));
95                    }
96                    field.set_values(values);
97                }
98            } else {
99                // set default (empty) value
100                field.empty();
101                if check_required && field.required {
102                    self.errors.insert(
103                        field.name.to_string(),
104                        String::from("no value for required field"));
105                }
106            }
107        }
108    }
109
110    /// Return a `Field` by name, or an error if there is not field by that
111    /// name.
112    pub fn field(self, name: &str) -> Result<Field<'a>, FormError> {
113        for field in self.fields {
114            if field.name == name {
115                return Ok(field);
116            }
117        }
118        Err(FormError::new(&format!("no field named {}", name)))
119    }
120
121    /// Return a list of values of a field, parsed to `T`. Returns an error
122    /// when the field is not found, when the values can not be converted
123    /// (parsed) or when the field has no value.
124    pub fn get<T>(&self, name: &str) ->
125            Result<Vec<T>, FormError>
126            where T: FromStr {
127        for field in &self.fields {
128            if field.name == name {
129                return match &field.values {
130                    Some(_) => {
131                        let mut converted: Vec<T> = Vec::new();
132                        for value in field.values() {
133                            converted.push(value.parse()?);
134                        }
135                        Ok(converted)
136                    },
137                    None => Err(FormError::new(
138                        &format!("field {} has no value", name))),
139                };
140            }
141        }
142        Err(FormError::new(
143            &format!("field {} not found", name)))
144    }
145
146    /// Return a single value for a field, parsed to `T`. Returns an error
147    /// when the field is not found, when more than one value is found, when
148    /// the value can not be converted (parsed) or when the field has no value.
149    pub fn getone<T>(&self, name: &str) ->
150            Result<T, FormError>
151            where T: FromStr {
152        for field in &self.fields {
153            if field.name == name {
154                return match &field.values {
155                    Some(_) => {
156                        let values = field.values();
157                        match values.len() {
158                            0 => {
159                                Err(FormError::new(
160                                    &format!(
161                                        "field {} has no value",
162                                        name)))
163                            },
164                            1 => {
165                                Ok(values[0].parse()?)
166                            },
167                            _ => {
168                                Err(FormError::new(
169                                    &format!(
170                                        "field {} has more than one value",
171                                        name)))
172                            },
173                        }
174                    },
175                    None => Err(FormError::new(
176                        &format!("field {} has no value", name))),
177                };
178            }
179        }
180        Err(FormError::new(&format!("field {} not found", name)))
181    }
182
183    /// Return a list of values of a field, as `String`s. Returns an error when
184    /// the field is not found or when the field has no value.
185    pub fn get_strings(&self, name: &str) -> Result<Vec<String>, FormError> {
186        for field in &self.fields {
187            if field.name == name {
188                return match &field.values {
189                    Some(values) => {
190                        Ok(values.iter().map(|v| v.as_string()).collect())
191                    },
192                    None => Err(FormError::new(
193                        &format!("field {} has no value", name))),
194                };
195            }
196        }
197        Err(FormError::new(
198            &format!("field {} not found", name)))
199    }
200
201    /// Return a single value for a field as `String`. Returns an error
202    /// when the field is not found, when more than one value is found,
203    /// or when the field has no value.
204    pub fn get_string(&self, name: &str) -> Result<String, FormError> {
205        self.getone::<String>(name)
206    }
207
208    /// Shortcut to create an `input` element, use this for non-collection
209    /// fields (so not for `InputType::Radio` or `InputType::Checkbox`,
210    /// for those see `choice_input()`). Returns self, so calls can
211    /// be chained.
212    pub fn input(
213            self, input_type: InputType, name: &'a str, label: &'a str,
214            required: bool, constraints: Vec<Constraint<'a>>,
215            attributes: Vec<Attr<'a>>)
216            -> Result<Self, FormError> {
217        self.element(
218            Element::Input(input_type), name, label, required, None,
219            &[], constraints, attributes)
220    }
221
222    /// Shortcut to create a set of `checkbox`es. Returns self, so calls
223    /// can be chained.
224    pub fn checkbox(
225            self, name: &'a str, label: &'a str,
226            required: bool, choices: &'a[(&'a str, &'a str)],
227            attributes: Vec<Attr<'a>>)
228            -> Result<Self, FormError> {
229        self.element(
230            Element::Input(InputType::Checkbox), name, label, required,
231            None, choices, vec![], attributes)
232    }
233
234    /// Shortcut to create a set of `radio` buttons. Returns self, so calls
235    /// can be chained.
236    pub fn radio(
237            self, name: &'a str, label: &'a str,
238            required: bool, choices: &'a[(&'a str, &'a str)],
239            attributes: Vec<Attr<'a>>)
240            -> Result<Self, FormError> {
241        self.element(
242            Element::Input(InputType::Radio), name, label, required,
243            None, choices, vec![], attributes)
244    }
245
246    /// Shortcut to create a text(-style) `input` with `datalist`
247    /// for auto-fill suggestions. Returns self, so calls can be chained.
248    pub fn datalist_input(
249            self, input_type: InputType, name: &'a str, label: &'a str,
250            required: bool, datalist: &'a[(&'a str, &'a str)],
251            attributes: Vec<Attr<'a>>)
252            -> Result<Self, FormError> {
253        match input_type {
254            InputType::Password |
255            InputType::Radio |
256            InputType::Checkbox |
257            InputType::File |
258            InputType::Hidden |
259            InputType::Button |
260            InputType::Submit |
261            InputType::Reset => {
262                return Err(FormError::new(
263                    &format!(
264                        "invalid input type {:?} for datalist input",
265                        input_type)));
266            },
267            _ => (),
268        }
269        self.element(
270            Element::Input(input_type), name, label, required, None,
271            datalist, vec![], attributes)
272    }
273
274    pub fn hidden(
275            self, name: &'a str, value: Option<&str>,
276            required: bool, constraints: Vec<Constraint<'a>>,
277            attributes: Vec<Attr<'a>>)
278            -> Result<Self, FormError> {
279        let values = match value {
280            Some(value) => Some(vec![value]),
281            None => None,
282        };
283        self.element(
284            Element::Input(InputType::Hidden), name, "", required, values,
285            &[], constraints, attributes)
286    }
287
288    /// Shortcut to create a `textarea` without validation. Returns self,
289    /// so calls can be chained.
290    pub fn textarea(
291            self, name: &'a str, label: &'a str, required: bool,
292            constraints: Vec<Constraint<'a>>, attributes: Vec<Attr<'a>>)
293            -> Result<Self, FormError> {
294        self.element(
295            Element::Textarea, name, label, required, None,
296            &[], constraints, attributes)
297    }
298
299    /// Shortcut to create a `select` dropdown. Returns self, so calls can
300    /// be chained.
301    pub fn select(
302            self, name: &'a str, label: &'a str, multi: bool,
303            required: bool, choices: &'a[(&'a str, &'a str)],
304            attributes: Vec<Attr<'a>>)
305            -> Result<Self, FormError> {
306        let element = Element::Select(
307            if multi {
308                SelectType::Single
309            } else {
310                SelectType::Multi
311            });
312        self.element(
313            element, name, label, required, None, choices, vec![],
314            attributes)
315    }
316
317    /// Shortcut to create a `submit` button. Returns self, so calls can be
318    /// chained.
319    pub fn submit(
320            self, name: Option<&'a str>, label: &'a str,
321            attributes: Vec<Attr<'a>>)
322            -> Result<Self, FormError> {
323        let name = match name {
324            Some(name) => name,
325            None => "",
326        };
327        self.element(
328            Element::Input(InputType::Submit), name, label, false, None,
329            &[], vec![], attributes)
330    }
331
332    /// Shortcut to create a `reset` button. Returns self, so calls can be
333    /// chained.
334    pub fn reset(
335            self, label: &'a str, attributes: Vec<Attr<'a>>)
336            -> Result<Self, FormError> {
337        self.element(
338            Element::Input(InputType::Submit), "", label, false, None,
339            &[], vec![], attributes)
340    }
341
342    /// Shortcut to create a `button` element. Returns self, so calls can be
343    /// chained.
344    pub fn button(
345            self, button_type: ButtonType, name: &'a str, label: &'a str,
346            attributes: Vec<Attr<'a>>)
347            -> Result<Self, FormError> {
348        self.element(
349            Element::Button(button_type), name, label, false, None,
350            &[], vec![], attributes)
351    }
352
353    /// Create an field of any type. This is similar to Field::new(),
354    /// but some checks and conversions are performed. Returns self, so
355    /// calls can be chained.
356    pub fn element(
357            mut self, element: Element, name: &'a str, label: &'a str,
358            required: bool, values: Option<Vec<&str>>,
359            choices: &'a[(&'a str, &'a str)],
360            constraints: Vec<Constraint<'a>>,
361            attributes: Vec<Attr<'a>>)
362            -> Result<Self, FormError> {
363        let values = match values {
364            Some(values) => {
365                let values: Vec<Value> = values.iter()
366                    .map(|v| Value::new(v))
367                    .collect();
368                for value in values.iter() {
369                    if let Err(e) = element.validate(&value) {
370                        return Err(FormError::new(&e.to_string()));
371                    }
372                }
373                Some(values)
374            },
375            None => None,
376        };
377        for constraint in constraints.iter() {
378            if !constraint.allowed_on(&element) {
379                return Err(FormError::new(
380                    &format!("constraint {:?} not allowed", constraint)));
381            }
382        }
383        for attribute in attributes.iter() {
384            if !attribute.allowed_on(&element) {
385                return Err(FormError::new(
386                    &format!("attribute {:?} not allowed", attribute)));
387            }
388        }
389        self.fields.push(Field::new(
390            name, label, element, required, values, choices,
391            constraints, attributes));
392        Ok(self)
393    }
394}
395
396impl <'a> Serialize for HtmlForm<'a> {
397    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
398            where S: Serializer {
399        let mut s = serializer.serialize_struct("Field", 4)?;
400        s.serialize_field("action", &self.action)?;
401        s.serialize_field("method", &self.method.attrvalue())?;
402        s.serialize_field("errors", &self.errors)?;
403        s.serialize_field("fields", &self.fields)?;
404        s.end()
405    }
406}
407
408/// Represents a form field/element.
409///
410/// A `Field` can convert itself to JSON (and other formats, using
411/// `serde`) and can perform validation of its `Value`s based on
412/// its `Constraint`s. Note that the `value` field always contains a `Vec`
413/// of `Value`s, close to how HTML url encoding works (where every key can
414/// appear more than once and every value is a string, or optional).
415#[derive(Debug)]
416pub struct Field<'a> {
417    name: &'a str,
418    label: &'a str,
419    element: Element,
420    required: bool,
421    choices: &'a[(&'a str, &'a str)],
422    values: Option<Vec<Value>>,
423    attributes: Vec<Attr<'a>>,
424    constraints: Vec<Constraint<'a>>,
425}
426
427impl <'a> Field<'a> {
428    /// Initialize a `Field`.
429    ///
430    /// It is advised to use the builder-style methods on HtmlForm to
431    /// instantiate, since those perform additional checks and prepare
432    /// arguments.
433    pub fn new(
434            name: &'a str, label: &'a str, element: Element,
435            required: bool, values: Option<Vec<Value>>,
436            choices: &'a[(&'a str, &'a str)],
437            constraints: Vec<Constraint<'a>>, attributes: Vec<Attr<'a>>)
438            -> Field<'a> {
439        Field {
440            name,
441            label,
442            element,
443            required,
444            choices,
445            constraints,
446            attributes,
447            values,
448        }
449    }
450
451    /// Returns a `Vec` of 0 or more non-empty values from self.values.
452    pub fn values(&self) -> Vec<Value> {
453        match &self.values {
454            None => Vec::new(),
455            Some(value) =>
456                value.iter()
457                    .filter(|value| value.as_string() != "")
458                    .map(|value| Value::new(&value.as_string()))
459                    .collect(),
460        }
461    }
462
463    /// Validate a set of values
464    ///
465    /// Note that this assumes `values` contains the correct amount of
466    /// non-empty values for this `Field`, so dealing with errors
467    /// regarding `self.required` and amount of values should be done before
468    /// this method is called.
469    ///
470    /// Generally, this method is not called directly, but indirectly by
471    /// `HtmlForm`'s `update()`.
472    pub fn validate(&self, values: &[&Value])
473            -> Result<(), ValidationError> {
474        // validate choices, but only for certain elements (on other
475        // elements, choices are (optional) suggestions, usually to
476        // populate the `datalist` element for auto-fill).
477        match self.element {
478            Element::Input(InputType::Checkbox) |
479            Element::Select(SelectType::Multi) => {
480                let choicevalues: Vec<&str> = self.choices.iter()
481                    .map(|(value, _)| {
482                        *value
483                    })
484                    .collect();
485                for value in values {
486                    let value_str = value.as_string();
487                    if !choicevalues.contains(&value_str.as_str()) {
488                        return Err(ValidationError::new(
489                            &format!(
490                                "{} is not a valid choice.", value_str)));
491                    }
492                    let split: Vec<&[&Value]> = values
493                        .split(|v| v == value)
494                        .collect();
495                    if split.len() > 2 {
496                        return Err(
497                            ValidationError::new(
498                                &format!(
499                                    "Value {} provided more than once.",
500                                    value_str)));
501                    }
502                }
503            },
504            _ => (),
505        }
506        for value in values {
507            self.element.validate(&value)?;
508            for constraint in self.constraints.iter() {
509                constraint.validate(&value)?;
510            }
511        }
512        Ok(())
513    }
514
515    /// Clear the `Field`'s values.
516    pub fn empty(&mut self) {
517        self.values = Some(Vec::new());
518    }
519
520    /// Set the `Field`'s values.
521    pub fn set_values(&mut self, values: Vec<&Value>) {
522        let mut clone = Vec::new();
523        for value in values {
524            clone.push(Value::new(&value.as_string()));
525        }
526        self.values = Some(clone);
527    }
528}
529
530impl <'a> Serialize for Field<'a> {
531    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
532            where S: Serializer {
533        let mut s = serializer.serialize_struct("Field", 9)?;
534        s.serialize_field("name", &self.name)?;
535        s.serialize_field("label", &self.label)?;
536        s.serialize_field("element", &self.element.element_name())?;
537        s.serialize_field("type", &self.element.element_type())?;
538        s.serialize_field("required", &self.required)?;
539        s.serialize_field("multi", &self.element.multi())?;
540        s.serialize_field("choices", &self.choices)?;
541
542        // attributes combines self.constraints and self.attributes
543        let mut attributesmap = HashMap::new();
544        for constraint in &self.constraints {
545            if let Some((name, value)) = constraint.attrpair() {
546                attributesmap.insert(name, value);
547            }
548        }
549        for attribute in self.attributes.iter() {
550            let (name, value) = attribute.attrpair();
551            attributesmap.insert(name, value);
552        }
553        s.serialize_field("attributes", &attributesmap)?;
554
555        // value is a single value (or None) if self.element.multi() == false,
556        // else it's a list
557        match &self.element.multi() {
558            true => {
559                s.serialize_field("value", &self.values)?;
560            },
561            false => {
562                match &self.values {
563                    Some(value) => {
564                        let strvalue = if value.len() == 1 {
565                            match self.element {
566                                Element::Input(InputType::Password) =>
567                                    String::new(),
568                                _ => value[0].as_string(),
569                            }
570                        } else {
571                            String::new()
572                        };
573                        s.serialize_field("value", &strvalue)?;
574                    },
575                    None => {
576                        s.serialize_field("value", &self.values)?;
577                    },
578                }
579            },
580        }
581        s.end()
582    }
583}