generated from mitchell/rust_template
add validation builder
This commit is contained in:
parent
360ef63b58
commit
bf04957370
14
src/form.rs
14
src/form.rs
@ -11,11 +11,11 @@ use web_sys::FormData;
|
||||
///
|
||||
/// This can be useful to use the same validation logic on the front
|
||||
/// end and backend without duplicating the logic.
|
||||
pub struct Validator<FD> {
|
||||
pub struct FormValidator<FD> {
|
||||
pub(crate) validations: Vec<Rc<dyn ValidationFn<FD>>>,
|
||||
}
|
||||
|
||||
impl<FD: FormToolData> Validator<FD> {
|
||||
impl<FD: FormToolData> FormValidator<FD> {
|
||||
/// Validates the given form data.
|
||||
///
|
||||
/// This runs all the validation functions for all the fields
|
||||
@ -40,8 +40,8 @@ pub struct Form<FD: FormToolData> {
|
||||
|
||||
impl<FD: FormToolData> Form<FD> {
|
||||
/// Gets the [`Validator`] for this form.
|
||||
pub fn validator(self) -> Validator<FD> {
|
||||
Validator {
|
||||
pub fn validator(self) -> FormValidator<FD> {
|
||||
FormValidator {
|
||||
validations: self.validations,
|
||||
}
|
||||
}
|
||||
@ -57,10 +57,10 @@ impl<FD: FormToolData> Form<FD> {
|
||||
}
|
||||
|
||||
/// Splits this [`Form`] into it's parts.
|
||||
pub fn to_parts(self) -> (RwSignal<FD>, Validator<FD>, View) {
|
||||
pub fn to_parts(self) -> (RwSignal<FD>, FormValidator<FD>, View) {
|
||||
(
|
||||
self.fd,
|
||||
Validator {
|
||||
FormValidator {
|
||||
validations: self.validations,
|
||||
},
|
||||
self.view,
|
||||
@ -131,7 +131,7 @@ pub trait FormToolData: Default + Clone + 'static {
|
||||
///
|
||||
/// However, the code to render the views are not configured out, it
|
||||
/// simply doesn't run, so the view needs to compile even on the server.
|
||||
fn get_validator() -> Validator<Self> {
|
||||
fn get_validator() -> FormValidator<Self> {
|
||||
let builder = FormBuilder::new(Self::default(), Self::Style::default());
|
||||
let builder = Self::build_form(builder);
|
||||
builder.validator()
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::{
|
||||
FieldSetter, ParseFn, RenderFn, UnparseFn, ValidationCb, ValidationFn,
|
||||
VanityControlBuilder, VanityControlData,
|
||||
},
|
||||
form::{Form, FormToolData, Validator},
|
||||
form::{Form, FormToolData, FormValidator},
|
||||
styles::FormStyle,
|
||||
};
|
||||
use leptos::{
|
||||
@ -305,8 +305,8 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn validator(&self) -> Validator<FD> {
|
||||
Validator {
|
||||
pub(crate) fn validator(&self) -> FormValidator<FD> {
|
||||
FormValidator {
|
||||
validations: self.validations.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
pub mod controls;
|
||||
pub mod form;
|
||||
pub mod form_builder;
|
||||
mod form;
|
||||
mod form_builder;
|
||||
pub mod styles;
|
||||
mod validation_builder;
|
||||
|
||||
pub use form::{Form, FormToolData, Validator};
|
||||
pub use form::{Form, FormToolData, FormValidator};
|
||||
pub use form_builder::FormBuilder;
|
||||
pub use validation_builder::ValidationBuilder;
|
||||
|
||||
140
src/validation_builder.rs
Normal file
140
src/validation_builder.rs
Normal file
@ -0,0 +1,140 @@
|
||||
use crate::controls::ValidationFn;
|
||||
use std::fmt::Display;
|
||||
|
||||
/// A helper builder that allows you to specify a validation function
|
||||
/// declaritivly
|
||||
///
|
||||
/// Using this builder is not required as validation functions can just be
|
||||
/// closures, but for simple validation function this builder can be helpful
|
||||
///
|
||||
/// Validations are run in the order that they are called in the builder.
|
||||
pub struct ValidationBuilder<T: 'static> {
|
||||
/// The name of the field, for error messages.
|
||||
name: String,
|
||||
/// The functions to be called when validating.
|
||||
functions: Vec<Box<dyn Fn(&str, &T) -> Result<(), String> + 'static>>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Default for ValidationBuilder<T> {
|
||||
fn default() -> Self {
|
||||
ValidationBuilder {
|
||||
name: String::from("Field"),
|
||||
functions: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> ValidationBuilder<T> {
|
||||
/// The name of the field that is being validated.
|
||||
///
|
||||
/// This is the name that will be used for error messages.
|
||||
pub fn named(mut self, name: impl ToString) -> Self {
|
||||
self.name = name.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the action validation function.
|
||||
pub fn build(self) -> impl ValidationFn<T> {
|
||||
move |value| {
|
||||
for f in self.functions.iter() {
|
||||
match f(self.name.as_str(), value) {
|
||||
Ok(()) => {}
|
||||
err => return err,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidationBuilder<String> {
|
||||
/// Requires the field to not be empty.
|
||||
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
|
||||
}
|
||||
|
||||
/// Requires the field's length to be at least `min_len`.
|
||||
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
|
||||
}
|
||||
|
||||
/// Requires the field's length to be less than or equal to `min_len`.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd<T> + Display + 'static> ValidationBuilder<T> {
|
||||
/// Requires the value to be at least `min_value` according to
|
||||
/// `PartialOrd`.
|
||||
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
|
||||
}
|
||||
|
||||
/// Requires the value to be at most `max_value` according to
|
||||
/// `PartialOrd`.
|
||||
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<T: PartialEq<T> + Display + 'static> ValidationBuilder<T> {
|
||||
/// Requires the field to be in the provided whitelist.
|
||||
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
|
||||
}
|
||||
|
||||
/// Requires the field to not be in the provided blacklist.
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user