Releases: greyblake/nutype
Nutype 0.5.0 with custom errors
Changes
- [FEATURE] Added support for custom error types and validation functions via the
error
andwith
attributes. - [BREAKING] Replaced
lazy_static
withstd::sync::LazyLock
for regex validation. This requires Rust 1.80 or higher and may cause compilation issues on older Rust versions due to the use ofstd::sync::LazyLock
. If upgrading Rust isn't an option, you can still uselazy_static
explicitly as a workaround. - [BREAKING] The fallible
::new()
constructor has been fully replaced by::try_new()
.
Highlights
Custom errors
Previously, custom validation logic in nutype could be achieved by passing a predicate
attribute, as shown below:
#[nutype(validate(predicate = |n| n % 2 == 1))]
struct OddNumber(i64);
This would automatically generate a simple error type:
enum OddNumberError {
PredicateViolated,
}
However, this approach often lacked flexibility. Many users needed more detailed error handling. For example, some users wanted to attach additional information to errors or provide more descriptive error messages. Others preferred to use a single error type across their application, but found it cumbersome to map very specific errors to a more general error type.
To address these needs, nutype now introduces the with
attribute for custom validation functions and the error
attribute for specifying custom error types:
use nutype::nutype;
// Define a newtype `Name` with custom validation logic and a custom error type `NameError`.
// If validation fails, `Name` cannot be instantiated.
#[nutype(
validate(with = validate_name, error = NameError),
derive(Debug, AsRef, PartialEq),
)]
struct Name(String);
// Custom error type for `Name` validation.
// You can use `thiserror` or similar crates to provide more detailed error messages.
#[derive(Debug, PartialEq)]
enum NameError {
TooShort { min: usize, length: usize },
TooLong { max: usize, length: usize },
}
// Validation function for `Name` that checks its length.
fn validate_name(name: &str) -> Result<(), NameError> {
const MIN: usize = 3;
const MAX: usize = 10;
let length = name.encode_utf16().count();
if length < MIN {
Err(NameError::TooShort { min: MIN, length })
} else if length > MAX {
Err(NameError::TooLong { max: MAX, length })
} else {
Ok(())
}
}
With this enhancement, users have full control over the error type and how errors are constructed during validation, making the error handling process more powerful and adaptable to different use cases.
Transition from fallible::new()
to ::try_new()
In version 0.4.3, the fallible ::new()
constructor was deprecated but still available. Now, it has been fully replaced by ::try_new()
. For example, to initialize a Name
from the previous example:
let name = Name::try_new("Anton").unwrap();
This change ensures a more consistent and explicit error-handling approach when creating instances.
Note that ::new()
is still used as a non-fallible constructor if there a newtype has no validation.
The sponsors β€οΈ
A big shoutout to the true sponsors of this release - my in-laws! Thanks for taking care of my wife and kid, giving me a free weekend to work on Nutype!
Links
Nutype 0.4.3
Changes
- Support generics
- [DEPRECATION] Fallible (when a newtype has validation) constructor
::new()
is deprecated. Users should use::try_new()
instead. - [FIX] Use absolute path for
::core::result::Result
when generating code forderive(TryFrom)
.
Highlights
This release comes with support of generic types for newtypes!
The example below defines SortedNotEmptyVec<T>
wrapper around Vec<T>
which is guaranteed to be not empty and sorted.
Note, that type bound T: Ord
enables invocation of v.sort()
in the sanitization function.
use nutype::nutype;
#[nutype(
sanitize(with = |mut v| { v.sort(); v }),
validate(predicate = |vec| !vec.is_empty()),
derive(Debug, PartialEq, AsRef),
)]
struct SortedNotEmptyVec<T: Ord>(Vec<T>);
let wise_friends = SortedNotEmptyVec::try_new(vec!["Seneca", "Zeno", "Plato"]).unwrap();
assert_eq!(wise_friends.as_ref(), &["Plato", "Seneca", "Zeno"]);
let numbers = SortedNotEmptyVec::try_new(vec![4, 2, 7, 1]).unwrap();
assert_eq!(numbers.as_ref(), &[1, 2, 4, 7]);
Links
Nutype 0.4.2
Changes
- Support
no_std
( the dependency needs to be declared asnutype = { default-features = false }
) - Support integration with
arbitrary
crate (seearbitrary
feature).- Support
Arbitrary
for integer types - Support
Arbitrary
for float types - Support
Arbitrary
for string inner types - Support
Arbitrary
for any inner types
- Support
- Possibility to specify boundaries (
greater
,greater_or_equal
,less
,less_or_equal
,len_char_min
,len_char_max
) with expressions or named constants. - Add
#[inline]
attribute to trivial functions - Improve error messages
Highlights
Here is an example of nutype and arbitrary playing together:
use nutype::nutype;
use arbtest::arbtest;
use arbitrary::Arbitrary;
#[nutype(
derive(Arbitrary, AsRef),
sanitize(trim),
validate(
not_empty,
len_char_max = 100,
),
)]
pub struct Title(String);
fn main() {
arbtest(|u| {
// Generate an arbitrary valid Title
let title = Title::arbitrary(u)?;
// The inner string is guaranteed to be non-empty
assert!(!title.as_ref().is_empty());
// The inner string is guaranteed not to exceed 100 characters
assert!(title.as_ref().chars().count() <= 100);
Ok(())
});
}
As you can see the derived implementation of Arbitrary
respects the validation rules.
In the similar way Arbitrary
can be derived for integer and float based types.
Links
Nutype 0.4.0
Acknowledgements
A heartfelt thanks to Daniyil Glushko for his invaluable assistance and exceptional work on this release. Daniyil, located in Zaporizhzhia, Ukraine, is a proficient Rust developer open to remote opportunities. I highly recommend reaching out to him for Rust development roles.
Changes
- [FEATURE] Support of arbitrary inner types with custom sanitizers and validators.
- [FEATURE] Add numeric validator
greater
- [FEATURE] Add numeric validator
less
- [BREAKING] Removal of asterisk derive
- [BREAKING] Use commas to separate high level attributes
- [BREAKING] Traits are derived with
#[nutype(derive(Debug))]
. The regular#[derive(Debug)]
syntax is not supported anymore. - [BREAKING] Validator
with
has been renamed topredicate
to reflect the boolean nature of its range - [BREAKING] String validator
min_len
has been renamed tolen_char_min
to reflect that is based on UTF8 chars. - [BREAKING] String validator
max_len
has been renamed tolen_char_max
to reflect that is based on UTF8 chars. - [BREAKING] Rename numeric validator
max
toless_or_equal
- [BREAKING] Rename numeric validator
min
togreater_or_equal
- [BREAKING] Rename error variants to follow the following formula:
<ValidationRule>Violated
. This implies the following renames:TooShort
->LenCharMinViolated
TooLong
->LenCharMaxViolated
Empty
->NotEmptyViolated
RegexMismatch
->RegexViolated
Invalid
->PredicateViolated
TooBig
->LessOrEqualViolated
TooSmall
->GreaterOrEqualViolated
NotFinite
->FiniteViolated
- Better error messages: in case of unknown attribute, validator or sanitizer the possible values are listed.
- [FIX] Make derived
Deserialize
work with RON format
Feature highlights
Arbitrary inner type
Previously #[nutype]
worked only with String
, integers and floats.
Now it's possible to use it with any arbitrary type (e.g. Vec<String>
):
#[nutype(
validate(predicate = |friends| !friends.is_empty() ),
)]
pub struct Frieds(Vec<String>);
New numeric validators
Instead of former min
and max
integers and floats can now be validated with:
greater_or_equal
- Inclusive lower boundgreater
- Exclusive lower boundless_or_equal
- Inclusive upper boundless
- Exclusive upper bound
Example:
#[nutype(
validate(
greater_or_equal = 0,
less_or_equal = 59,
),
)]
pub struct Minute(u8);
Derive
Deriving of traits now has to be done explicitly with #[nutype(derive(...))]
instead of #[derive(...)]
:
Example:
#[nutype(
validate(with = |n| n % 2 == 1),
derive(Debug, Clone, Copy)
)]
pub struct OddNumber(u64);
This makes it clear, that deriving is fully handled by #[nutype]
and prevents a potential confusion.
Links
Nutype 0.3.1
Changes
- Add ability to derive
Deref
on String, integer and float based types.
Examples
use nutype::nutype;
#[nutype]
#[derive(Deref)]
struct Email(String);
let email = Email::new("[email protected]")
// Call .len() which is delegated to the inner String due to the deref-coercion mechanism
assert_eq!(email.len(), 11);
Nutype 0.3.0
Changes
- [BREAKING]
min_len
andmax_len
validators run against number of characters in a string (val.chars().count()
), not number of bytes (val.len()
). - Add
finite
validation for float types which checks against NaN and infinity. - Support deriving of
Default
- Support deriving of
Eq
andOrd
on float types (iffinite
validation is present) - Support deriving of
TryFrom
for types without validation (in this case Error type isstd::convert::Infallible
)
Feature Highlights:
- Deriving Eq and Ord on f32 and f64 types: The new release addresses the limitation in Rust where
f32
andf64
types cannot implement theOrd
andEq
traits due to the presence ofNaN
values. Nutype introducesfinite
validation, which allows the correct implementation ofEq
andOrd
traits for float-based newtypes.
use nutype::nutype;
#[nutype(validate(finite))]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Distance(f64);
- Deriving Default: Nutype 0.3.0 introduces support for deriving the
Default
trait. This allows users to derive theDefault
trait for their custom types with validation logic. Nutype also generates a unit test to ensure the validity of the default value.
use nutype::nutype;
#[nutype(
validate(with = |n| n % 2 == 1)
default = 1
)]
#[derive(Debug, Default)]
pub struct OddNumber(u64);
Please note that dynamic validation makes it impossible to guarantee the validity of the default value at compile time. Panics will occur if an invalid default value is obtained.
For more details, refer to the Nutype documentation.
Blog post: Nutype 0.3.0 released
Nutype 0.2.0
Changes
- [BREAKING] Rename string validator
present
->not_empty
. Rename error variantMissing
->Empty
. - [BREAKING] Rename feature
serde1
toserde
. - Support string validation with
regex
(requiresregex
feature). - Introduce
new_unchecked
feature flag, that allows to bypass sanitization and validation. - Support derive of
JsonSchema
ofschemars
crate (requiresschemars08
feature).
Blog post
Please consider reading Nutype 0.2.0 is out! blog post, which covers in detail the regex
and new_unchecked
features.