use crate::{controls::ValidationFn, FormToolData};
use std::fmt::Display;
type ValidationBuilderFn<T> = dyn Fn(&str, &T) -> Result<(), String> + Send + Sync + 'static;
pub struct ValidationBuilder<FD: FormToolData, T: ?Sized + 'static> {
name: String,
field_fn: Box<dyn Fn(&FD) -> &T + Send + Sync + 'static>,
functions: Vec<Box<ValidationBuilderFn<T>>>,
}
impl<FD: FormToolData, T: ?Sized + 'static> ValidationBuilder<FD, T> {
pub fn for_field(field_fn: impl Fn(&FD) -> &T + Send + Sync + 'static) -> Self {
ValidationBuilder {
name: String::from("Field"),
field_fn: Box::new(field_fn),
functions: Vec::new(),
}
}
pub fn named(mut self, name: impl ToString) -> Self {
self.name = name.to_string();
self
}
pub fn custom(mut self, f: impl ValidationFn<T>) -> Self {
self.functions.push(Box::new(move |_name, value| f(value)));
self
}
pub fn build(self) -> impl ValidationFn<FD> {
move |form_data| {
let value = (self.field_fn)(form_data);
for f in self.functions.iter() {
match f(self.name.as_str(), value) {
Ok(()) => {}
err => return err,
}
}
Ok(())
}
}
}
impl<FD: FormToolData, T> ValidationBuilder<FD, Option<T>> {
pub fn required(mut self) -> Self {
self.functions.push(Box::new(move |name, value| {
if value.is_none() {
Err(format!("{} is required", name))
} else {
Ok(())
}
}));
self
}
}
impl<FD: FormToolData> ValidationBuilder<FD, str> {
pub fn required(mut self) -> Self {
self.functions.push(Box::new(move |name, value| {
if value.is_empty() {
Err(format!("{} is required", name))
} else {
Ok(())
}
}));
self
}
pub fn min_len(mut self, min_len: usize) -> Self {
self.functions.push(Box::new(move |name, value| {
if value.len() < min_len {
Err(format!("{} must be >= {} characters", name, min_len))
} else {
Ok(())
}
}));
self
}
pub fn max_len(mut self, max_len: usize) -> Self {
self.functions.push(Box::new(move |name, value| {
if value.len() > max_len {
Err(format!("{} must be <= {} characters", name, max_len))
} else {
Ok(())
}
}));
self
}
pub fn contains(mut self, pattern: impl ToString) -> Self {
let pattern = pattern.to_string();
self.functions.push(Box::new(move |name, value| {
if !value.contains(&pattern) {
Err(format!("{} must contain {}", name, &pattern))
} else {
Ok(())
}
}));
self
}
}
impl<FD: FormToolData, T: PartialOrd<T> + Display + Send + Sync + 'static>
ValidationBuilder<FD, T>
{
pub fn min_value(mut self, min_value: T) -> Self {
self.functions.push(Box::new(move |name, value| {
if value < &min_value {
Err(format!("{} mut be >= {}", name, min_value))
} else {
Ok(())
}
}));
self
}
pub fn max_value(mut self, max_value: T) -> Self {
self.functions.push(Box::new(move |name, value| {
if value > &max_value {
Err(format!("{} mut be <= {}", name, max_value))
} else {
Ok(())
}
}));
self
}
}
impl<FD: FormToolData, T: PartialEq<T> + Display + Send + Sync + 'static> ValidationBuilder<FD, T> {
pub fn whitelist(mut self, whitelist: Vec<T>) -> Self {
self.functions.push(Box::new(move |name, value| {
if !whitelist.contains(value) {
Err(format!("{} cannot be {}", name, value))
} else {
Ok(())
}
}));
self
}
pub fn blacklist(mut self, blacklist: Vec<T>) -> Self {
self.functions.push(Box::new(move |name, value| {
if blacklist.contains(value) {
Err(format!("{} cannot be {}", name, value))
} else {
Ok(())
}
}));
self
}
}