1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Library to validate and generate HTML forms.
//!
//! ```rust
//! use es_htmlform::{HtmlForm, FormError, ValidationError};
//! use es_htmlform::value::ValueMap;
//! use es_htmlform::types::{InputType, Method, Constraint as Cons, Attr};
//!
//! // simple form with 1 field
//! fn searchform() -> Result<HtmlForm<'static>, FormError> {
//! Ok(HtmlForm::new(".", Method::Get)
//! .input(
//! InputType::Text, "q", "Search", true,
//! vec![], vec![Attr::Placeholder("enter query")])?
//! .submit(None, "go!", vec![])?)
//! }
//!
//! // more elaborate example, with validation (both client- and
//! // server-side) and custom attributes
//! fn userform() -> Result<HtmlForm<'static>, FormError> {
//! Ok(HtmlForm::new(".", Method::Post)
//! .input(
//! InputType::Text, "username", "Username", true,
//! vec![
//! Cons::MinLength(5), Cons::MaxLength(16),
//! Cons::Pattern(r"^\w+$")],
//! vec![])?
//! .input(
//! InputType::Text, "name", "Full name", true,
//! vec![Cons::MinLength(0), Cons::MaxLength(64)], vec![])?
//! .input(
//! InputType::Password, "password", "Password", true,
//! vec![
//! Cons::MinLength(6), Cons::MaxLength(64),
//! Cons::Pattern(r"(\d.*[^\w\s\d]|[^\w\s\d].*\d)"),
//! ],
//! vec![Attr::Title(
//! "Must contain 1 number and 1 non-word character")])?
//! .input(
//! InputType::Number, "age", "Age", true,
//! vec![Cons::MinNumber(18.0)],
//! vec![Attr::StepInt(1), Attr::Any("id", "age")])?
//! .textarea("message", "Message", false, vec![], vec![])?
//! .hidden(
//! "csrf", Some("foo"), true,
//! vec![Cons::Func(Box::new(|v| {
//! if v.as_string().as_str() != "foo" {
//! Err(ValidationError::new("invalid CSRF token"))
//! } else {
//! Ok(())
//! }
//! }))],
//! vec![])?
//! .submit(None, "Submit", vec![])?
//! )
//! }
//!
//! fn main() {
//! let values = ValueMap::from_urlencoded(b"q=foo").unwrap();
//! let mut form = searchform().unwrap();
//! form.update(&values, true);
//!
//! println!("{}", serde_json::to_string_pretty(&form).unwrap());
//! assert_eq!(form.errors.len(), 0);
//! assert_eq!(form.get_string("q").unwrap(), "foo");
//! assert_eq!(
//! serde_json::to_string_pretty(&form).unwrap(),
//! r#"{
//! "action": ".",
//! "method": "get",
//! "errors": {},
//! "fields": [
//! {
//! "name": "q",
//! "label": "Search",
//! "element": "input",
//! "type": "text",
//! "required": true,
//! "multi": false,
//! "choices": [],
//! "attributes": {
//! "placeholder": "enter query"
//! },
//! "value": "foo"
//! },
//! {
//! "name": "",
//! "label": "go!",
//! "element": "input",
//! "type": "submit",
//! "required": false,
//! "multi": false,
//! "choices": [],
//! "attributes": {},
//! "value": ""
//! }
//! ]
//! }"#);
//!
//! let values = ValueMap::from_urlencoded(
//! b"username=johnny&name=Johnny&password=foobar-123&age=46&csrf=bar"
//! ).unwrap();
//! let mut form = userform().unwrap();
//! form.update(&values, true);
//!
//! assert_eq!(form.errors.len(), 1);
//! assert_eq!(
//! form.errors.get("csrf").unwrap(), "invalid CSRF token");
//! assert_eq!(form.get_string("username").unwrap(), "johnny");
//! assert_eq!(form.get_string("password").unwrap(), "foobar-123");
//! assert_eq!(form.get_string("csrf").unwrap(), "bar");
//! }
//! ```
pub use crate;
pub use crate;