use std::str::FromStr;
use std::collections::HashMap;
use serde::ser::{Serialize, Serializer, SerializeStruct};
use crate::error::{FormError, ValidationError};
use crate::value::{ValueMap, Value};
use crate::types::{
Method, Element, InputType, SelectType, ButtonType, Constraint, Attr};
#[derive(Debug)]
pub struct HtmlForm<'a> {
pub action: &'a str,
pub method: Method,
pub errors: HashMap<String, String>,
pub fields: Vec<Field<'a>>,
}
impl <'a> HtmlForm<'a> {
pub fn new(action: &'a str, method: Method) -> HtmlForm<'a> {
HtmlForm {
action,
method,
errors: HashMap::new(),
fields: Vec::new(),
}
}
pub fn update(&mut self, values: &ValueMap, check_required: bool) {
self.errors.drain();
for field in &mut self.fields {
if let Some(values) = values.values(&field.name) {
if !field.element.multi() && values.len() > 1 {
self.errors.insert(
field.name.to_string(),
String::from("field can only have one value"));
} else {
let values: Vec<&Value> = values.iter()
.map(|v| v)
.collect();
if let Err(e) = field.validate(&values) {
self.errors.insert(
field.name.to_string(), format!("{}", e));
}
field.set_values(values);
}
} else {
field.empty();
if check_required && field.required {
self.errors.insert(
field.name.to_string(),
String::from("no value for required field"));
}
}
}
}
pub fn field(self, name: &str) -> Result<Field<'a>, FormError> {
for field in self.fields {
if field.name == name {
return Ok(field);
}
}
Err(FormError::new(&format!("no field named {}", name)))
}
pub fn get<T>(&self, name: &str) ->
Result<Vec<T>, FormError>
where T: FromStr {
for field in &self.fields {
if field.name == name {
return match &field.values {
Some(_) => {
let mut converted: Vec<T> = Vec::new();
for value in field.values() {
converted.push(value.parse()?);
}
Ok(converted)
},
None => Err(FormError::new(
&format!("field {} has no value", name))),
};
}
}
Err(FormError::new(
&format!("field {} not found", name)))
}
pub fn getone<T>(&self, name: &str) ->
Result<T, FormError>
where T: FromStr {
for field in &self.fields {
if field.name == name {
return match &field.values {
Some(_) => {
let values = field.values();
match values.len() {
0 => {
Err(FormError::new(
&format!(
"field {} has no value",
name)))
},
1 => {
Ok(values[0].parse()?)
},
_ => {
Err(FormError::new(
&format!(
"field {} has more than one value",
name)))
},
}
},
None => Err(FormError::new(
&format!("field {} has no value", name))),
};
}
}
Err(FormError::new(&format!("field {} not found", name)))
}
pub fn get_strings(&self, name: &str) -> Result<Vec<String>, FormError> {
for field in &self.fields {
if field.name == name {
return match &field.values {
Some(values) => {
Ok(values.iter().map(|v| v.as_string()).collect())
},
None => Err(FormError::new(
&format!("field {} has no value", name))),
};
}
}
Err(FormError::new(
&format!("field {} not found", name)))
}
pub fn get_string(&self, name: &str) -> Result<String, FormError> {
self.getone::<String>(name)
}
pub fn input(
self, input_type: InputType, name: &'a str, label: &'a str,
required: bool, constraints: Vec<Constraint<'a>>,
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
self.element(
Element::Input(input_type), name, label, required, None,
&[], constraints, attributes)
}
pub fn checkbox(
self, name: &'a str, label: &'a str,
required: bool, choices: &'a[(&'a str, &'a str)],
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
self.element(
Element::Input(InputType::Checkbox), name, label, required,
None, choices, vec![], attributes)
}
pub fn radio(
self, name: &'a str, label: &'a str,
required: bool, choices: &'a[(&'a str, &'a str)],
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
self.element(
Element::Input(InputType::Radio), name, label, required,
None, choices, vec![], attributes)
}
pub fn datalist_input(
self, input_type: InputType, name: &'a str, label: &'a str,
required: bool, datalist: &'a[(&'a str, &'a str)],
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
match input_type {
InputType::Password |
InputType::Radio |
InputType::Checkbox |
InputType::File |
InputType::Hidden |
InputType::Button |
InputType::Submit |
InputType::Reset => {
return Err(FormError::new(
&format!(
"invalid input type {:?} for datalist input",
input_type)));
},
_ => (),
}
self.element(
Element::Input(input_type), name, label, required, None,
datalist, vec![], attributes)
}
pub fn hidden(
self, name: &'a str, value: Option<&str>,
required: bool, constraints: Vec<Constraint<'a>>,
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
let values = match value {
Some(value) => Some(vec![value]),
None => None,
};
self.element(
Element::Input(InputType::Hidden), name, "", required, values,
&[], constraints, attributes)
}
pub fn textarea(
self, name: &'a str, label: &'a str, required: bool,
constraints: Vec<Constraint<'a>>, attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
self.element(
Element::Textarea, name, label, required, None,
&[], constraints, attributes)
}
pub fn select(
self, name: &'a str, label: &'a str, multi: bool,
required: bool, choices: &'a[(&'a str, &'a str)],
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
let element = Element::Select(
if multi {
SelectType::Single
} else {
SelectType::Multi
});
self.element(
element, name, label, required, None, choices, vec![],
attributes)
}
pub fn submit(
self, name: Option<&'a str>, label: &'a str,
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
let name = match name {
Some(name) => name,
None => "",
};
self.element(
Element::Input(InputType::Submit), name, label, false, None,
&[], vec![], attributes)
}
pub fn reset(
self, label: &'a str, attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
self.element(
Element::Input(InputType::Submit), "", label, false, None,
&[], vec![], attributes)
}
pub fn button(
self, button_type: ButtonType, name: &'a str, label: &'a str,
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
self.element(
Element::Button(button_type), name, label, false, None,
&[], vec![], attributes)
}
pub fn element(
mut self, element: Element, name: &'a str, label: &'a str,
required: bool, values: Option<Vec<&str>>,
choices: &'a[(&'a str, &'a str)],
constraints: Vec<Constraint<'a>>,
attributes: Vec<Attr<'a>>)
-> Result<Self, FormError> {
let values = match values {
Some(values) => {
let values: Vec<Value> = values.iter()
.map(|v| Value::new(v))
.collect();
for value in values.iter() {
if let Err(e) = element.validate(&value) {
return Err(FormError::new(&e.to_string()));
}
}
Some(values)
},
None => None,
};
for constraint in constraints.iter() {
if !constraint.allowed_on(&element) {
return Err(FormError::new(
&format!("constraint {:?} not allowed", constraint)));
}
}
for attribute in attributes.iter() {
if !attribute.allowed_on(&element) {
return Err(FormError::new(
&format!("attribute {:?} not allowed", attribute)));
}
}
self.fields.push(Field::new(
name, label, element, required, values, choices,
constraints, attributes));
Ok(self)
}
}
impl <'a> Serialize for HtmlForm<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let mut s = serializer.serialize_struct("Field", 4)?;
s.serialize_field("action", &self.action)?;
s.serialize_field("method", &self.method.attrvalue())?;
s.serialize_field("errors", &self.errors)?;
s.serialize_field("fields", &self.fields)?;
s.end()
}
}
#[derive(Debug)]
pub struct Field<'a> {
name: &'a str,
label: &'a str,
element: Element,
required: bool,
choices: &'a[(&'a str, &'a str)],
values: Option<Vec<Value>>,
attributes: Vec<Attr<'a>>,
constraints: Vec<Constraint<'a>>,
}
impl <'a> Field<'a> {
pub fn new(
name: &'a str, label: &'a str, element: Element,
required: bool, values: Option<Vec<Value>>,
choices: &'a[(&'a str, &'a str)],
constraints: Vec<Constraint<'a>>, attributes: Vec<Attr<'a>>)
-> Field<'a> {
Field {
name,
label,
element,
required,
choices,
constraints,
attributes,
values,
}
}
pub fn values(&self) -> Vec<Value> {
match &self.values {
None => Vec::new(),
Some(value) =>
value.iter()
.filter(|value| value.as_string() != "")
.map(|value| Value::new(&value.as_string()))
.collect(),
}
}
pub fn validate(&self, values: &[&Value])
-> Result<(), ValidationError> {
match self.element {
Element::Input(InputType::Checkbox) |
Element::Select(SelectType::Multi) => {
let choicevalues: Vec<&str> = self.choices.iter()
.map(|(value, _)| {
*value
})
.collect();
for value in values {
let value_str = value.as_string();
if !choicevalues.contains(&value_str.as_str()) {
return Err(ValidationError::new(
&format!(
"{} is not a valid choice.", value_str)));
}
let split: Vec<&[&Value]> = values
.split(|v| v == value)
.collect();
if split.len() > 2 {
return Err(
ValidationError::new(
&format!(
"Value {} provided more than once.",
value_str)));
}
}
},
_ => (),
}
for value in values {
self.element.validate(&value)?;
for constraint in self.constraints.iter() {
constraint.validate(&value)?;
}
}
Ok(())
}
pub fn empty(&mut self) {
self.values = Some(Vec::new());
}
pub fn set_values(&mut self, values: Vec<&Value>) {
let mut clone = Vec::new();
for value in values {
clone.push(Value::new(&value.as_string()));
}
self.values = Some(clone);
}
}
impl <'a> Serialize for Field<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let mut s = serializer.serialize_struct("Field", 9)?;
s.serialize_field("name", &self.name)?;
s.serialize_field("label", &self.label)?;
s.serialize_field("element", &self.element.element_name())?;
s.serialize_field("type", &self.element.element_type())?;
s.serialize_field("required", &self.required)?;
s.serialize_field("multi", &self.element.multi())?;
s.serialize_field("choices", &self.choices)?;
let mut attributesmap = HashMap::new();
for constraint in &self.constraints {
if let Some((name, value)) = constraint.attrpair() {
attributesmap.insert(name, value);
}
}
for attribute in self.attributes.iter() {
let (name, value) = attribute.attrpair();
attributesmap.insert(name, value);
}
s.serialize_field("attributes", &attributesmap)?;
match &self.element.multi() {
true => {
s.serialize_field("value", &self.values)?;
},
false => {
match &self.values {
Some(value) => {
let strvalue = if value.len() == 1 {
match self.element {
Element::Input(InputType::Password) =>
String::new(),
_ => value[0].as_string(),
}
} else {
String::new()
};
s.serialize_field("value", &strvalue)?;
},
None => {
s.serialize_field("value", &self.values)?;
},
}
},
}
s.end()
}
}