Initial commit
This commit is contained in:
commit
33ca42a731
8 changed files with 431 additions and 0 deletions
116
src/lib.rs
Normal file
116
src/lib.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//! Provides an interface for validation of arbitrary types.
|
||||
//!
|
||||
//! The `Vet` trait requires an implementation of a `VetError` type alias and
|
||||
//! an `is_valid` method, which executes arbitrary validation logic and returns
|
||||
//! a result indicating whether the instance is valid.
|
||||
//!
|
||||
//! The `Vet` trait also provides a default implementation of a `vet` method
|
||||
//! which, dependant on the result of `is_valid`, either wraps the instance with
|
||||
//! a `Vetted<T>` struct or propagates the validation error.
|
||||
//!
|
||||
//! The `Vetted<T>` wrapper guarantees that the inner value was successfully
|
||||
//! validated and remains immutable as long as it is wrapped.
|
||||
//!
|
||||
//! Implementations for the common standard library types `Vec<T>` and
|
||||
//! `Option<T>` are provided.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use vet::{Vet, Vetted};
|
||||
//!
|
||||
//! #[derive(Debug)]
|
||||
//! struct Username(String);
|
||||
//!
|
||||
//! #[derive(Debug, PartialEq)]
|
||||
//! enum UsernameVetError {
|
||||
//! TooShort,
|
||||
//! TooLong,
|
||||
//! InvalidChar,
|
||||
//! }
|
||||
//!
|
||||
//! impl Vet for Username {
|
||||
//! type VetError = UsernameVetError;
|
||||
//!
|
||||
//! fn is_valid(&self) -> Result<(), Self::VetError> {
|
||||
//! if self.0.len() < 3 {
|
||||
//! return Err(Self::VetError::TooShort);
|
||||
//! }
|
||||
//! if self.0.len() > 32 {
|
||||
//! return Err(Self::VetError::TooLong);
|
||||
//! }
|
||||
//! if self.0.chars().any(|c| !c.is_alphanumeric()) {
|
||||
//! return Err(Self::VetError::InvalidChar);
|
||||
//! }
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let username = Username(String::from("hi"));
|
||||
//! assert_eq!(username.is_valid(), Err(UsernameVetError::TooShort));
|
||||
//!
|
||||
//! let username = Username(String::from("benjamin"));
|
||||
//! match username.vet() {
|
||||
//! Ok(username) => create_account(username),
|
||||
//! Err(error) => println!("Could not create account: {:?}", error),
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn create_account(username: Vetted<Username>) {
|
||||
//! println!("Account {:?} created", username);
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use core::ops::Deref;
|
||||
|
||||
/// A wrapper around a validated instance
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Vetted<T>(T);
|
||||
|
||||
impl<T> Deref for Vetted<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface for arbitrary type validation
|
||||
pub trait Vet {
|
||||
/// The error returned by failed validation
|
||||
type VetError;
|
||||
|
||||
/// Executes arbitrary validation logic on this instance.
|
||||
fn is_valid(&self) -> Result<(), Self::VetError>;
|
||||
|
||||
/// Validates this instance and results in a wrapped instance if successful.
|
||||
fn vet(self) -> Result<Vetted<Self>, Self::VetError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match self.is_valid() {
|
||||
Ok(()) => Ok(Vetted(self)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Vet> Vet for Option<T> {
|
||||
type VetError = T::VetError;
|
||||
|
||||
fn is_valid(&self) -> Result<(), Self::VetError> {
|
||||
match self {
|
||||
Some(t) => t.is_valid(),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Vet> Vet for Vec<T> {
|
||||
type VetError = T::VetError;
|
||||
|
||||
fn is_valid(&self) -> Result<(), Self::VetError> {
|
||||
self.iter().try_for_each(|t| t.is_valid())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue