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)]
|
||||
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 {
|
||||
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
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
|
@ -82,34 +88,42 @@ impl ResultBuilder {
|
|||
}
|
||||
|
||||
/// 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
|
||||
N: Into<String>,
|
||||
N: Into<&'a str>,
|
||||
F: FnOnce(FieldResultBuilder<T>) -> FieldResultBuilder<T>,
|
||||
{
|
||||
let builder = check(FieldResultBuilder {
|
||||
val,
|
||||
errors: Vec::with_capacity(0),
|
||||
});
|
||||
let name = name.into();
|
||||
self.errors.extend(
|
||||
builder
|
||||
.errors
|
||||
.into_iter()
|
||||
.map(|msg| (name.clone(), msg.clone())),
|
||||
);
|
||||
builder.append_errors(name.into(), &mut self.errors);
|
||||
self
|
||||
}
|
||||
|
||||
/// Validate a field that implements [`ValidateField`]
|
||||
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
|
||||
}
|
||||
|
||||
/// Obtain the final result of the validation
|
||||
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 {
|
||||
errors: self.errors,
|
||||
}
|
||||
.into())
|
||||
} else {
|
||||
Ok(success_val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +140,14 @@ impl<'a, T> FieldResultBuilder<'a, T> {
|
|||
}
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue