Nutype est une macro Proc qui permet d'ajouter des contraintes supplémentaires telles que la désinfection et la validation au modèle NewType ordinaire. Le code généré empêche d'instancier une valeur sans passer les chèques. Cela fonctionne de cette façon même avec la désérialisation serde .
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 ) ,
) ;Pour plus, veuillez consulter:
Les désinfectants, les validateurs et les traits dérivés disponibles sont déterminés par le type intérieur, qui entre dans les catégories suivantes:
u8 , u16 , u32 , u64 , u128 , i8 , i16 , i32 , i64 , i128 , usize , isize )f32 , f64 ) Au moment où le type intérieur de chaîne prend en charge uniquement le type String (détenue).
| Désinfectant | Description | Exemple |
|---|---|---|
trim | Supprime les espaces blancs et traînants | trim |
lowercase | Convertit la chaîne en minuscules | lowercase |
uppercase | Convertit la chaîne en majuscules | uppercase |
with | Désinfectant personnalisé. Une fonction ou une fermeture qui reçoit String et renvoie String | with = |mut s: String| ( s.truncate(5); s ) |
| Validateur | Description | Variante d'erreur | Exemple |
|---|---|---|---|
len_char_min | Longueur min de la chaîne (en caractères, pas octets) | LenCharMinViolated | len_char_min = 5 |
len_char_max | Longueur maximale de la chaîne (en caractères, pas octets) | LenCharMaxViolated | len_char_max = 255 |
not_empty | Rejette une chaîne vide | NotEmptyViolated | not_empty |
regex | Valide le format avec un regex. Nécessite une fonctionnalité regex . | RegexViolated | regex = "^[0-9]{7}$" ou regex = ID_REGEX |
predicate | Validateur personnalisé. Une fonction ou une fermeture qui reçoit &str et retourne bool | PredicateViolated | predicate = |s: &str| s.contains('@') |
with | Validateur personnalisé avec une erreur personnalisée | N / A | (Voir l'exemple ci-dessous) |
Exigences:
regex de nutype est activée.regex comme dépendance.Il existe plusieurs façons d'utiliser Regex.
Une expression régulière peut être définie directement en place:
# [ nutype ( validate ( regex = "^[0-9]{3}-[0-9]{3}$" ) ) ]
pub struct PhoneNumber ( String ) ; ou il peut être défini avec 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 ) ; ou il peut être défini avec 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 ) ; ou 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 ) ; Les traits suivants peuvent être dérivés pour un type basé sur des chaînes: Debug , Clone , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , From Deserialize TryFrom , Hash , Into , Borrow , Default , Display , Serialize .
Les types intérieurs entiers sont: u8 , u16 , u32 , u64 , u128 , i8 , i16 , i32 , i64 , i128 , using isize usize
| Désinfectant | Description | Exemple |
|---|---|---|
with | Désinfectant personnalisé. | with = |raw| raw.clamp(0, 100) |
| Validateur | Description | Variante d'erreur | Exemple |
|---|---|---|---|
less | Bound supérieur exclusif | LessViolated | less = 100 |
less_or_equal | Bound supérieur inclusif | LessOrEqualViolated | less_or_equal = 99 |
greater | Bound inférieur exclusif | GreaterViolated | greater = 17 |
greater_or_equal | Bound inférieur inclusif | GreaterOrEqualViolated | greater_or_equal = 18 |
predicate | Prédicat personnalisé | PredicateViolated | predicate = |num| num % 2 == 0 |
with | Validateur personnalisé avec une erreur personnalisée | N / A | (Voir l'exemple ci-dessous) |
Les traits suivants peuvent être dérivés pour un type basé sur des entiers: Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , Into , From , TryFrom , Hash , Borrow , Display , Default , Serialize , Deserialize .
Les types intérieurs flottants sont: f32 , f64 .
| Désinfectant | Description | Exemple |
|---|---|---|
with | Désinfectant personnalisé. | with = |val| val.clamp(0.0, 100.0) |
| Validateur | Description | Variante d'erreur | Exemple |
|---|---|---|---|
less | Bound supérieur exclusif | LessViolated | less = 100.0 |
less_or_equal | Bound supérieur inclusif | LessOrEqualViolated | less_or_equal = 100.0 |
greater | Bound inférieur exclusif | GreaterViolated | greater = 0.0 |
greater_or_equal | Bound inférieur inclusif | GreaterOrEqualViolated | greater_or_equal = 0.0 |
finite | Vérifier contre Nan et Infinity | FiniteViolated | finite |
predicate | Prédicat personnalisé | PredicateViolated | predicate = |val| val != 50.0 |
with | Validateur personnalisé avec une erreur personnalisée | N / A | (Voir l'exemple ci-dessous) |
Les traits suivants peuvent être dérivés pour un type basé sur le flotteur: Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , Into , From , TryFrom , Hash , Borrow , Display , Default , Serialize , Deserialize .
Il est également possible de dériver Eq et Ord si les règles de validation garantissent que NaN est exclu. Cela peut être fait en appliquant par validation finite . Par exemple:
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
struct Size ( f64 ) ; Pour tout autre type, il est possible de définir des désinfectants personnalisés with et des validations personnalisées avec predicate :
use nutype :: nutype ;
# [ nutype (
derive ( Debug , PartialEq , AsRef , Deref ) ,
sanitize ( with = | mut guests| { guests . sort ( ) ; guests } ) ,
validate ( predicate = |guests| !guests . is_empty ( ) ) ,
) ]
pub struct GuestList ( Vec < String > ) ;Il est également possible d'utiliser des génériques:
# [ nutype (
sanitize ( with = | mut v| { v . sort ( ) ; v } ) ,
validate ( predicate = |vec| !vec . is_empty ( ) ) ,
derive ( Debug , PartialEq , AsRef , Deref ) ,
) ]
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" ] ) ;
assert_eq ! ( wise_friends . len ( ) , 3 ) ;
let numbers = SortedNotEmptyVec :: try_new ( vec ! [ 4 , 2 , 7 , 1 ] ) . unwrap ( ) ;
assert_eq ! ( numbers . as_ref ( ) , & [ 1 , 2 , 4 , 7 ] ) ;
assert_eq ! ( numbers . len ( ) , 4 ) ; Vous pouvez définir des désinfectants personnalisés en utilisant l'option with . Un désinfectant personnalisé est une fonction ou une fermeture qui reçoit une valeur d'un type intérieur avec la propriété et renvoie une valeur désinfectée.
Par exemple, celui-ci
# [ nutype ( sanitize ( with = new_to_old ) ) ]
pub struct CityName ( String ) ;
fn new_to_old ( s : String ) -> String {
s . replace ( "New" , "Old" )
}est égal à celui suivant:
# [ nutype ( sanitize ( with = |s| s . replace ( "New" , "Old" ) ) ) ]
pub struct CityName ( String ) ;Et fonctionne de la même manière:
let city = CityName :: new ( "New York" ) ;
assert_eq ! ( city . into_inner ( ) , "Old York" ) ; De la même manière, il est possible de définir des validateurs personnalisés, mais une fonction de validation reçoit une référence et renvoie bool . Considérez-le comme un prédicat.
# [ nutype ( validate ( predicate = is_valid_name ) ) ]
pub struct Name ( String ) ;
fn is_valid_name ( name : & str ) -> bool {
// A fancy way to verify if the first character is uppercase
name . chars ( ) . next ( ) . map ( char :: is_uppercase ) . unwrap_or ( false )
} Pour définir votre propre type d'erreur et implémenter une logique de validation personnalisée, vous pouvez combiner les attributs with et error :
// Define a custom error type for validation failures.
// Although it's best practice to implement `std::error::Error` for custom error types,
// we are omitting that for simplicity here.
# [ derive ( Debug , PartialEq ) ]
enum NameError {
TooShort ,
TooLong ,
}
// Define a custom validation function for `Name`.
// The function returns `Result<(), NameError>`, where `Ok(())` indicates a valid name,
// and `Err(NameError)` represents a specific validation failure.
fn validate_name ( name : & str ) -> Result < ( ) , NameError > {
if name . len ( ) < 3 {
Err ( NameError :: TooShort )
} else if name . len ( ) > 10 {
Err ( NameError :: TooLong )
} else {
Ok ( ( ) )
}
}
// Define a newtype `Name` with custom validation logic and custom error.
# [ nutype (
validate ( with = validate_name , error = NameError ) ,
derive ( Debug , PartialEq ) ,
) ]
struct Name ( String ) ; Il est important de s'assurer que le type spécifié dans l'attribut error correspond au type d'erreur renvoyé par la fonction de validation.
Default # [ nutype (
derive ( Default ) ,
default = "Anonymous" ,
) ]
pub struct Name ( String ) ;Eq et Ord sur flotteur Avec Nutype, il est possible de dériver Eq et Ord s'il y a un ensemble de validation finite . La validation finite garantit que la valeur valide exclut NaN .
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
pub struct Weight ( f64 ) ; Il est découragé, mais il est possible de contourner les contraintes en activant la fonctionnalité de caisse new_unchecked et en marquant un type avec new_unchecked :
# [ nutype (
new_unchecked ,
sanitize ( trim ) ,
validate ( len_char_min = 8 )
) ]
pub struct Name ( String ) ;
// Yes, you're forced to use `unsafe` here, so everyone will point fingers at YOU.
let name = unsafe { Name :: new_unchecked ( " boo " . to_string ( ) ) } ;
// `name` violates the sanitization and validation rules!!!
assert_eq ! ( name . into_inner ( ) , " boo " ) ; arbitrary - permet dériver de arbitrary::Arbitrary .new_unchecked - permet la génération de la fonction de danget ::new_unchecked() .regex - permet d'utiliser regex = validation sur les types basés sur des chaînes. Remarque: Votre caisse doit également avoir explicitement regex dans ses dépendances.serde - intégrations avec caisse serde . Permet de dériver des traits Serialize et Deserialize .schemars08 - Permet de dériver le trait JsonSchema de la caisse de schémas. Notez qu'à l'heure actuelle, les règles de validation ne sont pas respectées.std - activé par défaut. Utilisez default-features = false pour désactiver. Aujourd'hui, je vis à Berlin, j'ai le luxe de vivre une vie physiquement sûre. Mais je suis ukrainien. Les 25 premières années de ma vie que j'ai passées à Kharkiv, la deuxième plus grande ville d'Ukraine, à 60 km de la frontière avec la Russie. Aujourd'hui, environ un tiers de ma ville natale est détruite par les Russes. Mes parents, mes proches et mes amis ont dû survivre à l'artillerie et à l'attaque aérienne, vivant pendant plus d'un mois dans les sous-sols.
Certains d'entre eux ont réussi à évacuer vers l'UE. Certains autres essaient de vivre des "vies normales" à Kharkiv, y faisant des tâches quotidiennes. Et certains sont en première ligne en ce moment, risquant leur vie à chaque seconde pour protéger les autres.
Je vous encourage à faire un don à la Charity Foundation de Serhiy Prytula. Choisissez simplement le projet que vous aimez et faites un don. C'est l'une des fondations les plus connues, vous pouvez regarder un petit documentaire à ce sujet. Votre contribution à la force militaire ukrainienne est une contribution à mon calme, donc je peux passer plus de temps à développer le projet.
Merci.
MIT © Serhii Potapov