Rename error type alias

This commit is contained in:
Nettika 2022-07-21 11:21:36 -07:00
parent 5c983a0ac0
commit af44f3e4d2
3 changed files with 53 additions and 40 deletions

View file

@ -11,47 +11,60 @@ Add a dependency entry in `Cargo.toml`.
vet = "0.1" vet = "0.1"
``` ```
```rust Implement the `Vet` trait on your type.
use vet::Vet;
```rust
use vet::{Valid, Vet};
// A valid username consists of between 3 and 32 alphanumeric characters
#[derive(Debug)] #[derive(Debug)]
struct Username(String); struct Username(String);
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum UsernameVetError { enum InvalidUsername {
TooShort, TooShort, // Under 3 characters
TooLong, TooLong, // Over 3 characters
InvalidChar, InvalidChar, // Contains non-alphanumeric character
} }
impl Vet for Username { impl Vet for Username {
type VetError = UsernameVetError; type Error = InvalidUsername;
fn is_valid(&self) -> Result<(), Self::VetError> {
// Arbitrary logic to validate the Username type
fn is_valid(&self) -> Result<(), Self::Error> {
if self.0.len() < 3 { if self.0.len() < 3 {
return Err(Self::VetError::TooShort); return Err(Self::Error::TooShort);
} }
if self.0.len() > 32 { if self.0.len() > 32 {
return Err(Self::VetError::TooLong); return Err(Self::Error::TooLong);
} }
if self.0.chars().any(|c| !c.is_alphanumeric()) { if self.0.chars().any(|c| !c.is_alphanumeric()) {
return Err(Self::VetError::InvalidChar); return Err(Self::Error::InvalidChar);
} }
Ok(()) Ok(())
} }
} }
```
Vetted types provide safety guarantees for the types contents.
```rust
fn main() { fn main() {
let username = Username(String::from("hi")); let args: Vec<String> = env::args().collect();
assert_eq!(username.is_valid(), Err(UsernameVetError::TooShort)); let username = Username(args[1].clone());
let username = Username(String::from("benjamin")); // If successfully vetted, the username will be wrapped in a `Valid` struct
match username.vet() { match username.vet() {
Ok(username) => create_account(username), Ok(username) => create_account(username),
Err(error) => println!("Could not create account: {:?}", error), Err(InvalidUsername::TooShort) => eprintln!("Username too short! (3 min)"),
Err(InvalidUsername::TooLong) => eprintln!("Username too long! (32 max)"),
Err(InvalidUsername::InvalidChar) => eprintln!("Username contains invalid characters!"),
} }
} }
fn create_account(username: Vetted<Username>) { // Any `Valid<Username>` passed is guaranteed
// to have met the arbitrary validation checks.
fn create_account(username: Valid<Username>) {
println!("Account {:?} created", username); println!("Account {:?} created", username);
} }
``` ```

View file

@ -1,8 +1,8 @@
//! Provides an interface for validation of arbitrary types. //! Provides an interface for validation of arbitrary types.
//! //!
//! The `Vet` trait requires an implementation of a `VetError` type alias and //! The `Vet` trait requires an `Error` type alias and an implementation of an
//! an `is_valid` method, which executes arbitrary validation logic and returns //! `is_valid` method, which executes arbitrary validation logic and returns a
//! a result indicating whether the instance is valid. //! result indicating whether the instance is valid.
//! //!
//! The `Vet` trait also provides a default implementation of a `vet` method //! 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 //! which, dependant on the result of `is_valid`, either wraps the instance with
@ -11,8 +11,8 @@
//! The `Valid<T>` wrapper guarantees that the inner value was successfully //! The `Valid<T>` wrapper guarantees that the inner value was successfully
//! validated and remains immutable as long as it is wrapped. //! validated and remains immutable as long as it is wrapped.
//! //!
//! Implementations for the common standard library types `Vec<T>` and //! Implementations for generic arrays, and for the common standard library
//! `Option<T>` are provided. //! types `Vec<T>` and `Option<T>` are provided.
//! //!
//! # Examples //! # Examples
//! //!
@ -23,24 +23,24 @@
//! struct Username(String); //! struct Username(String);
//! //!
//! #[derive(Debug, PartialEq)] //! #[derive(Debug, PartialEq)]
//! enum UsernameVetError { //! enum InvalidUsername {
//! TooShort, //! TooShort,
//! TooLong, //! TooLong,
//! InvalidChar, //! InvalidChar,
//! } //! }
//! //!
//! impl Vet for Username { //! impl Vet for Username {
//! type VetError = UsernameVetError; //! type Error = InvalidUsername;
//! //!
//! fn is_valid(&self) -> Result<(), Self::VetError> { //! fn is_valid(&self) -> Result<(), Self::Error> {
//! if self.0.len() < 3 { //! if self.0.len() < 3 {
//! return Err(Self::VetError::TooShort); //! return Err(Self::Error::TooShort);
//! } //! }
//! if self.0.len() > 32 { //! if self.0.len() > 32 {
//! return Err(Self::VetError::TooLong); //! return Err(Self::Error::TooLong);
//! } //! }
//! if self.0.chars().any(|c| !c.is_alphanumeric()) { //! if self.0.chars().any(|c| !c.is_alphanumeric()) {
//! return Err(Self::VetError::InvalidChar); //! return Err(Self::Error::InvalidChar);
//! } //! }
//! Ok(()) //! Ok(())
//! } //! }
@ -48,7 +48,7 @@
//! //!
//! fn main() { //! fn main() {
//! let username = Username(String::from("hi")); //! let username = Username(String::from("hi"));
//! assert_eq!(username.is_valid(), Err(UsernameVetError::TooShort)); //! assert_eq!(username.is_valid(), Err(InvalidUsername::TooShort));
//! //!
//! let username = Username(String::from("benjamin")); //! let username = Username(String::from("benjamin"));
//! match username.vet() { //! match username.vet() {
@ -85,13 +85,13 @@ impl<T> core::ops::Deref for Valid<T> {
/// An interface for arbitrary type validation /// An interface for arbitrary type validation
pub trait Vet { pub trait Vet {
/// The error returned by failed validation /// The error returned by failed validation
type VetError; type Error;
/// Executes arbitrary validation logic on this instance. /// Executes arbitrary validation logic on this instance.
fn is_valid(&self) -> Result<(), Self::VetError>; fn is_valid(&self) -> Result<(), Self::Error>;
/// Validates this instance and results in a wrapped instance if successful. /// Validates this instance and results in a wrapped instance if successful.
fn vet(self) -> Result<Valid<Self>, Self::VetError> fn vet(self) -> Result<Valid<Self>, Self::Error>
where where
Self: Sized, Self: Sized,
{ {
@ -103,17 +103,17 @@ pub trait Vet {
} }
impl<T: Vet, const N: usize> Vet for [T; N] { impl<T: Vet, const N: usize> Vet for [T; N] {
type VetError = T::VetError; type Error = T::Error;
fn is_valid(&self) -> Result<(), Self::VetError> { fn is_valid(&self) -> Result<(), Self::Error> {
self.iter().try_for_each(|i| i.is_valid()) self.iter().try_for_each(|i| i.is_valid())
} }
} }
impl<T: Vet> Vet for Option<T> { impl<T: Vet> Vet for Option<T> {
type VetError = T::VetError; type Error = T::Error;
fn is_valid(&self) -> Result<(), Self::VetError> { fn is_valid(&self) -> Result<(), Self::Error> {
match self { match self {
Some(o) => o.is_valid(), Some(o) => o.is_valid(),
None => Ok(()), None => Ok(()),
@ -132,9 +132,9 @@ impl<T: Vet> Valid<Option<T>> {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<T: Vet> Vet for alloc::vec::Vec<T> { impl<T: Vet> Vet for alloc::vec::Vec<T> {
type VetError = T::VetError; type Error = T::Error;
fn is_valid(&self) -> Result<(), Self::VetError> { fn is_valid(&self) -> Result<(), Self::Error> {
self.iter().try_for_each(|t| t.is_valid()) self.iter().try_for_each(|t| t.is_valid())
} }
} }

View file

@ -7,8 +7,8 @@ struct EvenUsize(usize);
struct OddUsize; struct OddUsize;
impl Vet for EvenUsize { impl Vet for EvenUsize {
type VetError = OddUsize; type Error = OddUsize;
fn is_valid(&self) -> Result<(), Self::VetError> { fn is_valid(&self) -> Result<(), Self::Error> {
if self.0 % 2 == 0 { if self.0 % 2 == 0 {
Ok(()) Ok(())
} else { } else {