Rename error type alias
This commit is contained in:
parent
40a3160e9a
commit
cb57718f94
3 changed files with 53 additions and 40 deletions
45
readme.md
45
readme.md
|
|
@ -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);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
42
src/lib.rs
42
src/lib.rs
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue