Nutype
Rust newtype with guarantees πΊπ¦ π¦
Install / Use
/learn @greyblake/NutypeREADME
Nutype is a proc macro that allows adding extra constraints like sanitization and validation to the regular newtype pattern. The generated code makes it impossible to instantiate a value without passing the checks. It works this way even with serde deserialization.
- Quick start
- Inner types (String | Integer | Float | Other)
- Custom (sanitizers | validators | errors)
- Deriving traits
- Constants
- Recipes
- Constructor visibility
- Breaking constraints with new_unchecked
- Feature Flags
- Support Ukrainian military forces
- Similar projects
Quick start
use nutype::nutype;
// Define newtype Username
#[nutype(
sanitize(trim, lowercase),
validate(not_empty, len_char_max = 20),
derive(Debug, PartialEq, Clone),
)]
pub struct Username(String);
// We can obtain a value of Username with `::try_new()`.
// Note that Username holds a sanitized string
assert_eq!(
Username::try_new(" FooBar ").unwrap().into_inner(),
"foobar"
);
// It's impossible to obtain an invalid Username
// Note that we also got `UsernameError` enum generated implicitly
// based on the validation rules.
assert_eq!(
Username::try_new(" "),
Err(UsernameError::NotEmptyViolated),
);
assert_eq!(
Username::try_new("TheUserNameIsVeryVeryLong"),
Err(UsernameError::LenCharMaxViolated),
);
For more please see:
Inner types
Available sanitizers, validators, and derivable traits are determined by the inner type, which falls into the following categories:
- String
- Integer (
u8,u16,u32,u64,u128,i8,i16,i32,i64,i128,usize,isize) - Float (
f32,f64) - Anything else
String
At the moment the string inner type supports only String (owned) type.
String sanitizers
| Sanitizer | Description | Example |
|-------------|-------------------------------------------------------------------------------------|-------------------------------------------------|
| trim | Removes leading and trailing whitespaces | trim |
| lowercase | Converts the string to lowercase | lowercase |
| uppercase | Converts the string to uppercase | uppercase |
| with | Custom sanitizer. A function or closure that receives String and returns String | with = \|mut s: String\| ( s.truncate(5); s ) |
String validators
| Validator | Description | Error variant | Example |
|-----------------|---------------------------------------------------------------------------------|-----------------------|----------------------------------------------|
| len_char_min | Min length of the string (in chars, not bytes) | LenCharMinViolated | len_char_min = 5 |
| len_char_max | Max length of the string (in chars, not bytes) | LenCharMaxViolated | len_char_max = 255 |
| len_utf16_min | Min length of the string in UTF-16 code units (useful for JavaScript interop) | LenUtf16MinViolated | len_utf16_min = 5 |
| len_utf16_max | Max length of the string in UTF-16 code units (useful for JavaScript interop) | LenUtf16MaxViolated | len_utf16_max = 255 |
| not_empty | Rejects an empty string | NotEmptyViolated | not_empty |
| regex | Validates format with a regex. Requires regex feature. | RegexViolated | regex = "^[0-9]{7}$" or regex = ID_REGEX |
| predicate | Custom validator. A function or closure that receives &str and returns bool | PredicateViolated | predicate = \|s: &str\| s.contains('@') |
| with | Custom validator with a custom error | N/A | (see example below) |
Regex validation
Requirements:
regexfeature ofnutypeis enabled.- You have to explicitly include
regexas a dependency.
There are a number of ways you can use regex.
A regular expression can be defined right in place:
#[nutype(validate(regex = "^[0-9]{3}-[0-9]{3}$"))]
pub struct PhoneNumber(String);
or it can be defined with std::sync::LazyLock:
use regex::Regex;
static PHONE_NUMBER_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new("^[0-9]{3}-[0-9]{3}$").unwrap());
#[nutype(validate(regex = PHONE_NUMBER_REGEX))]
pub struct PhoneNumber(String);
or it can be defined with lazy_static:
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref PHONE_NUMBER_REGEX: Regex = Regex::new("^[0-9]{3}-[0-9]{3}$").unwrap();
}
#[nutype(validate(regex = PHONE_NUMBER_REGEX))]
pub struct PhoneNumber(String);
or once_cell:
use once_cell::sync::Lazy;
use regex::Regex;
static PHONE_NUMBER_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new("[0-9]{3}-[0-9]{3}$").unwrap());
#[nutype(validate(regex = PHONE_NUMBER_REGEX))]
pub struct PhoneNumber(String);
String derivable traits
The following traits can be derived for a string-based type:
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, FromStr, AsRef, Deref,
From, TryFrom, Into, Hash, Borrow, Display, Default, Serialize, Deserialize.
Integer
The integer inner types are: u8, u16,u32, u64, u128, i8, i16, i32, i64, i128, usize, isize.
Integer sanitizers
| Sanitizer | Description | Example |
|-----------|-------------------|------------------------------------|
| with | Custom sanitizer. | with = \|raw\| raw.clamp(0, 100) |
Integer validators
| Validator | Description | Error variant | Example |
| ------------------- | ------------------------------------- | ------------------------- | ------------------------------------ |
| less | Exclusive upper bound | LessViolated | less = 100 |
| less_or_equal | Inclusive upper bound | LessOrEqualViolated | less_or_equal = 99 |
| greater | Exclusive lower bound | GreaterViolated | greater = 17 |
| greater_or_equal | Inclusive lower bound | GreaterOrEqualViolated | greater_or_equal = 18 |
| predicate | Custom predicate | PredicateViolated | predicate = \|num\| num % 2 == 0 |
| with | Custom validator with a custom error | N/A | (see example below) |
Integer derivable traits
The following traits can be derived for an integer-based type:
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromStr, AsRef, Deref,
Into, From, TryFrom, Hash, Borrow, Display, Default, Serialize, Deserialize.
Float
The float inner types are: f32, f64.
Float sanitizers
| Sanitizer | Description | Example |
|-----------|-------------------|----------------------------------------|
| with | Custom sanitizer. | with = \|val\| val.clamp(0.0, 100.0) |
Float validators
| Validator | Description | Error variant | Example |
| ------------------ | ------------------------------------ | ------------------------ | ----------------------------------- |
| less | Exclusive upper bound | LessViolated | less = 100.0 |
| less_or_equal | Inclusive upper bound | LessOrEqualViolated | less_or_equal = 100.0
