vet/src/lib.rs

124 lines
3.3 KiB
Rust
Raw Normal View History

2022-07-04 02:26:48 -07:00
//! 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
2022-07-09 12:15:03 -07:00
//! a `Valid<T>` struct or propagates the validation error.
2022-07-04 02:26:48 -07:00
//!
2022-07-09 12:15:03 -07:00
//! The `Valid<T>` wrapper guarantees that the inner value was successfully
2022-07-04 02:26:48 -07:00
//! 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
//!
//! ```
2022-07-09 12:15:03 -07:00
//! use vet::{Valid, Vet};
2022-07-04 02:26:48 -07:00
//!
//! #[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),
//! }
//! }
//!
2022-07-09 12:15:03 -07:00
//! fn create_account(username: Valid<Username>) {
2022-07-04 02:26:48 -07:00
//! println!("Account {:?} created", username);
//! }
//! ```
2022-07-04 22:25:13 -07:00
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
2022-07-04 02:26:48 -07:00
2022-07-09 13:06:29 -07:00
#[cfg(test)]
mod tests;
2022-07-04 02:26:48 -07:00
/// A wrapper around a validated instance
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
2022-07-09 12:15:03 -07:00
pub struct Valid<T>(T);
2022-07-04 02:26:48 -07:00
2022-07-09 12:15:03 -07:00
impl<T> core::ops::Deref for Valid<T> {
2022-07-04 02:26:48 -07:00
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.
2022-07-09 12:15:03 -07:00
fn vet(self) -> Result<Valid<Self>, Self::VetError>
2022-07-04 02:26:48 -07:00
where
Self: Sized,
{
match self.is_valid() {
2022-07-09 12:15:03 -07:00
Ok(()) => Ok(Valid(self)),
2022-07-04 02:26:48 -07:00
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(()),
}
}
}
2022-07-04 22:25:13 -07:00
#[cfg(feature = "alloc")]
impl<T: Vet> Vet for alloc::vec::Vec<T> {
2022-07-04 02:26:48 -07:00
type VetError = T::VetError;
fn is_valid(&self) -> Result<(), Self::VetError> {
self.iter().try_for_each(|t| t.is_valid())
}
}