util: add ValidateField trait
This commit is contained in:
parent
4be3bdda0c
commit
27ccba5252
1 changed files with 35 additions and 13 deletions
|
@ -10,11 +10,17 @@ pub struct Sane<T>(T);
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Insane<T>(T);
|
pub struct Insane<T>(T);
|
||||||
|
|
||||||
/// Anything that can be validated (used for transforming [Insane] to [Sane])
|
/// Anything that can be validated (used for transforming [`Insane`] to [`Sane`])
|
||||||
pub trait Validate: Sized {
|
pub trait Validate: Sized {
|
||||||
fn validate(&self, builder: ResultBuilder) -> ResultBuilder;
|
fn validate(&self, builder: ResultBuilder) -> ResultBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A field in a struct that can be validated and that is reused in multiple places.
|
||||||
|
/// Primarily useful for newtype structs.
|
||||||
|
pub trait ValidateField<'a>: Sized {
|
||||||
|
fn validate_field(builder: FieldResultBuilder<'a, Self>) -> FieldResultBuilder<'a, Self>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Validation error
|
/// Validation error
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
|
@ -82,34 +88,42 @@ impl ResultBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Begin sanitization on a single field
|
/// Begin sanitization on a single field
|
||||||
pub fn field<T, N, F>(mut self, val: &T, name: N, check: F) -> Self
|
pub fn field<'a, T, N, F>(mut self, val: &'a T, name: N, check: F) -> Self
|
||||||
where
|
where
|
||||||
N: Into<String>,
|
N: Into<&'a str>,
|
||||||
F: FnOnce(FieldResultBuilder<T>) -> FieldResultBuilder<T>,
|
F: FnOnce(FieldResultBuilder<T>) -> FieldResultBuilder<T>,
|
||||||
{
|
{
|
||||||
let builder = check(FieldResultBuilder {
|
let builder = check(FieldResultBuilder {
|
||||||
val,
|
val,
|
||||||
errors: Vec::with_capacity(0),
|
errors: Vec::with_capacity(0),
|
||||||
});
|
});
|
||||||
let name = name.into();
|
builder.append_errors(name.into(), &mut self.errors);
|
||||||
self.errors.extend(
|
self
|
||||||
builder
|
}
|
||||||
.errors
|
|
||||||
.into_iter()
|
/// Validate a field that implements [`ValidateField`]
|
||||||
.map(|msg| (name.clone(), msg.clone())),
|
pub fn field_auto<'a, T, N>(mut self, val: &'a T, name: N) -> Self
|
||||||
);
|
where
|
||||||
|
T: ValidateField<'a>,
|
||||||
|
N: Into<&'a str>,
|
||||||
|
{
|
||||||
|
let builder = T::validate_field(FieldResultBuilder {
|
||||||
|
val,
|
||||||
|
errors: Vec::with_capacity(0),
|
||||||
|
});
|
||||||
|
builder.append_errors(name.into(), &mut self.errors);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the final result of the validation
|
/// Obtain the final result of the validation
|
||||||
fn result<T>(self, success_val: T) -> Result<T> {
|
fn result<T>(self, success_val: T) -> Result<T> {
|
||||||
if self.errors.len() > 0 {
|
if self.errors.is_empty() {
|
||||||
|
Ok(success_val)
|
||||||
|
} else {
|
||||||
Err(Error {
|
Err(Error {
|
||||||
errors: self.errors,
|
errors: self.errors,
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
} else {
|
|
||||||
Ok(success_val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +140,14 @@ impl<'a, T> FieldResultBuilder<'a, T> {
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn append_errors(self, field_name: &str, errors: &mut Vec<(String, String)>) {
|
||||||
|
errors.extend(
|
||||||
|
self.errors
|
||||||
|
.into_iter()
|
||||||
|
.map(|msg| (String::from(field_name), msg)),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
|
Loading…
Reference in a new issue