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