Rust newtype with guarantees πΊπ¦ π¦
MIT License
Bot releases are hidden (Show)
::new()
is deprecated. Users should use ::try_new()
instead.::core::result::Result
when generating code for derive(TryFrom)
.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]);
Published by greyblake 6 months ago
no_std
( the dependency needs to be declared as nutype = { default-features = false }
)arbitrary
crate (see arbitrary
feature).
Arbitrary
for integer typesArbitrary
for float typesArbitrary
for string inner typesArbitrary
for any inner typesgreater
, greater_or_equal
, less
, less_or_equal
, len_char_min
, len_char_max
) with expressions or named constants.#[inline]
attribute to trivial functionsHere 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.
Published by greyblake 11 months ago
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.
greater
less
#[nutype(derive(Debug))]
. The regular #[derive(Debug)]
syntax is not supported anymore.with
has been renamed to predicate
to reflect the boolean nature of its rangemin_len
has been renamed to len_char_min
to reflect that is based on UTF8 chars.max_len
has been renamed to len_char_max
to reflect that is based on UTF8 chars.max
to less_or_equal
min
to greater_or_equal
<ValidationRule>Violated
. This implies the following renames:
TooShort
-> LenCharMinViolated
TooLong
-> LenCharMaxViolated
Empty
-> NotEmptyViolated
RegexMismatch
-> RegexViolated
Invalid
-> PredicateViolated
TooBig
-> LessOrEqualViolated
TooSmall
-> GreaterOrEqualViolated
NotFinite
-> FiniteViolated
Deserialize
work with RON formatPreviously #[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>);
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 boundExample:
#[nutype(
validate(
greater_or_equal = 0,
less_or_equal = 59,
),
)]
pub struct Minute(u8);
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.
Published by greyblake over 1 year ago
Deref
on String, integer and float based types.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);
Published by greyblake over 1 year ago
min_len
and max_len
validators run against number of characters in a string (val.chars().count()
), not number of bytes (val.len()
).finite
validation for float types which checks against NaN and infinity.Default
Eq
and Ord
on float types (if finite
validation is present)TryFrom
for types without validation (in this case Error type is std::convert::Infallible
)f32
and f64
types cannot implement the Ord
and Eq
traits due to the presence of NaN
values. Nutype introduces finite
validation, which allows the correct implementation of Eq
and Ord
traits for float-based newtypes.use nutype::nutype;
#[nutype(validate(finite))]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Distance(f64);
Default
trait. This allows users to derive the Default
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
Published by greyblake over 1 year ago
present
-> not_empty
. Rename error variant Missing
-> Empty
.serde1
to serde
.regex
(requires regex
feature).new_unchecked
feature flag, that allows to bypass sanitization and validation.JsonSchema
of schemars
crate (requires schemars08
feature).Please consider reading Nutype 0.2.0 is out! blog post, which covers in detail the regex
and new_unchecked
features.