Nutype ist ein Proc -Makro, mit dem zusätzliche Einschränkungen wie Desinfektion und Validierung zum regulären New -Typ -Muster hinzugefügt werden können. Der generierte Code macht es unmöglich, einen Wert zu instanziieren, ohne die Schecks zu übermitteln. Es funktioniert so auch bei der Deserialisierung 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 ) ,
) ;Weitere Informationen finden Sie unter:
Verfügbare Desinfektionsmittel, Validatoren und Ableitungsmerkmale werden durch den inneren Typ bestimmt, der in die folgenden Kategorien fällt:
u8 , u16 , u32 , u64 , u128 , i8 , i16 , i32 , i64 , i128 , usize , isize )f32 , f64 ) Derzeit unterstützt der innere String -Typ nur String -Typ (im Besitz).
| Desinfektionsmittel | Beschreibung | Beispiel |
|---|---|---|
trim | Entfernt die Führung und Verlauf von Whitespaces | trim |
lowercase | Konvertiert die Saite in Kleinbuchstaben | lowercase |
uppercase | Konvertiert die Schnur in Großbuchstaben | uppercase |
with | Sonderangehörige. Eine Funktion oder Schließung, die String empfängt und String zurückgibt | with = |mut s: String| ( s.truncate(5); s ) |
| Validator | Beschreibung | Fehlervariante | Beispiel |
|---|---|---|---|
len_char_min | Min Länge der Saite (in Zeichen, nicht Bytes) | LenCharMinViolated | len_char_min = 5 |
len_char_max | Maximale Länge der Saite (in Zeichen, nicht Bytes) | LenCharMaxViolated | len_char_max = 255 |
not_empty | Lehnt eine leere Zeichenfolge ab | NotEmptyViolated | not_empty |
regex | Validiert das Format mit einem Regex. Benötigt regex -Funktion. | RegexViolated | regex = "^[0-9]{7}$" oder regex = ID_REGEX |
predicate | Benutzerdefinierter Validator. Eine Funktion oder Schließung, die bool empfängt &str und zurückgibt | PredicateViolated | predicate = |s: &str| s.contains('@') |
with | Benutzerdefinierter Validator mit einem benutzerdefinierten Fehler | N / A | (Siehe Beispiel unten) |
Anforderungen:
regex -Funktion von nutype ist aktiviert.regex ausdrücklich als Abhängigkeit einbeziehen.Es gibt eine Reihe von Möglichkeiten, wie Sie Regex verwenden können.
Ein regulärer Ausdruck kann direkt an Ort und Stelle definiert werden:
# [ nutype ( validate ( regex = "^[0-9]{3}-[0-9]{3}$" ) ) ]
pub struct PhoneNumber ( String ) ; oder es kann mit std::sync::LazyLock definiert werden:
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 ) ; oder es kann mit lazy_static definiert werden:
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 ) ; oder 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 ) ; Die folgenden Merkmale können für einen String-basierten Typ abgeleitet werden: Debug , Clone , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , From , TryFrom , Into , Hash , Borrow , Display , Default , Serialize , Deserialize .
Die Ganzzahl -inneren Typen sind: u8 , u16 , u32 , u64 , u128 , i8 , i16 , i32 , i64 , i128 , usize , isize .
| Desinfektionsmittel | Beschreibung | Beispiel |
|---|---|---|
with | Sonderangehörige. | with = |raw| raw.clamp(0, 100) |
| Validator | Beschreibung | Fehlervariante | Beispiel |
|---|---|---|---|
less | Exklusive Obergrenze | LessViolated | less = 100 |
less_or_equal | Inklusive Obergrenze | LessOrEqualViolated | less_or_equal = 99 |
greater | Exklusive Untergrenze | GreaterViolated | greater = 17 |
greater_or_equal | Inklusive Untergrenze | GreaterOrEqualViolated | greater_or_equal = 18 |
predicate | Benutzerdefiniertes Prädikat | PredicateViolated | predicate = |num| num % 2 == 0 |
with | Benutzerdefinierter Validator mit einem benutzerdefinierten Fehler | N / A | (Siehe Beispiel unten) |
Die folgenden Merkmale können für einen integer basierten Typ abgeleitet werden: Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , Into , From TryFrom , Hash , Borrow , Display , Default , Serialize , Deserialize .
Die inneren Innenarten sind: f32 , f64 .
| Desinfektionsmittel | Beschreibung | Beispiel |
|---|---|---|
with | Sonderangehörige. | with = |val| val.clamp(0.0, 100.0) |
| Validator | Beschreibung | Fehlervariante | Beispiel |
|---|---|---|---|
less | Exklusive Obergrenze | LessViolated | less = 100.0 |
less_or_equal | Inklusive Obergrenze | LessOrEqualViolated | less_or_equal = 100.0 |
greater | Exklusive Untergrenze | GreaterViolated | greater = 0.0 |
greater_or_equal | Inklusive Untergrenze | GreaterOrEqualViolated | greater_or_equal = 0.0 |
finite | Überprüfen Sie gegen Nan und Unendlichkeit | FiniteViolated | finite |
predicate | Benutzerdefiniertes Prädikat | PredicateViolated | predicate = |val| val != 50.0 |
with | Benutzerdefinierter Validator mit einem benutzerdefinierten Fehler | N / A | (Siehe Beispiel unten) |
Die folgenden Merkmale können für einen Float-basierten Typ abgeleitet werden: Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , Into , From TryFrom , Hash , Borrow , Display , Default , Serialize , Deserialize .
Es ist auch möglich, Eq und Ord abzuleiten, wenn die Validierungsregeln garantieren, dass NaN ausgeschlossen ist. Dies kann durch finite Validierung erfolgen. Zum Beispiel:
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
struct Size ( f64 ) ; Für jeden anderen Typ ist es möglich, benutzerdefinierte Desinfektionsmittel with und benutzerdefinierte Validierungen mit predicate zu definieren:
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 > ) ;Es ist auch möglich, Generika zu verwenden:
# [ 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 ) ; Sie können benutzerdefinierte Desinfektionsmittel mit der with -Option festlegen. Ein benutzerdefinierter Desinfektionsmittel ist eine Funktion oder Schließung, die einen Wert eines inneren Typs mit Eigentum erhält und einen Desinfizitenwert zurückgibt.
Zum Beispiel dieses
# [ nutype ( sanitize ( with = new_to_old ) ) ]
pub struct CityName ( String ) ;
fn new_to_old ( s : String ) -> String {
s . replace ( "New" , "Old" )
}ist gleich dem folgenden:
# [ nutype ( sanitize ( with = |s| s . replace ( "New" , "Old" ) ) ) ]
pub struct CityName ( String ) ;Und funktioniert genauso:
let city = CityName :: new ( "New York" ) ;
assert_eq ! ( city . into_inner ( ) , "Old York" ) ; In ähnlicher Weise ist es möglich, benutzerdefinierte Validatoren zu definieren, aber eine Validierungsfunktion erhält eine Referenz und gibt bool zurück. Betrachten Sie es als Prädikat.
# [ 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 )
} Um Ihren eigenen Fehlertyp zu definieren und benutzerdefinierte Validierungslogik zu implementieren, können Sie die with und error kombinieren:
// 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 ) ; Es ist wichtig sicherzustellen, dass der im error angegebene Typ mit dem von der Validierungsfunktion zurückgegebenen Fehlertyp übereinstimmt.
Default abgeben # [ nutype (
derive ( Default ) ,
default = "Anonymous" ,
) ]
pub struct Name ( String ) ;Eq und Ord auf Float -Typen ab Bei Nutypen ist es möglich, Eq und Ord abzuleiten, wenn ein finite Validierungssatz vorliegt. Die finite Validierung stellt sicher, dass der gültige Wert NaN ausschließt.
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
pub struct Weight ( f64 ) ; Es ist entmutigt, aber es ist möglich, die Einschränkungen zu umgehen, indem Sie die Funktion new_unchecked Crate" aktivieren und einen Typ mit new_unchecked markieren:
# [ 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 - ermöglicht das Ableiten von arbitrary::Arbitrary .new_unchecked - Ermöglicht die Erzeugung von unsicher ::new_unchecked() .regex - Ermöglicht die Verwendung regex = Validierung für Zeichenfolgenbasis. Hinweis: Ihre Kiste muss auch explizit regex in seinen Abhängigkeiten haben.serde - Integrationen mit serde . Ermöglicht die Ableitung Serialize und Deserialize .schemars08 - Ermöglicht das Ableiten von JsonSchema -Merkmalen der Schemarskiste. Beachten Sie, dass derzeit Validierungsregeln nicht respektiert werden.std - standardmäßig aktiviert. Verwenden Sie default-features = false um zu deaktivieren. Heute lebe ich in Berlin, ich habe den Luxus, ein körperlich sicheres Leben zu führen. Aber ich bin ukrainisch. Die ersten 25 Jahre meines Lebens, die ich in Kharkiv, der zweitgrößten Stadt in der Ukraine, 60 km von der Grenze zu Russland entfernt habe. Heute wird etwa ein Drittel meiner Heimatstadt von Russen zerstört. Meine Eltern, meine Verwandten und meine Freunde mussten die Artillerie und den Luftangriff überleben und über einen Monat in Kellern leben.
Einige von ihnen haben es geschafft, zur EU zu evakuieren. Einige andere versuchen, in Kharkiv "normales Leben" zu führen und dort tägliche Aufgaben zu erledigen. Und einige sind gerade an vorderster Front und riskieren jede Sekunde ihr Leben, um den Rest zu schützen.
Ich ermutige Sie, an die Charity Foundation von Serhiy Prytula zu spenden. Wählen Sie einfach das Projekt aus, das Sie mögen und spenden. Dies ist eine der bekanntesten Fundamente. Sie können sich einen kleinen Dokumentarfilm darüber ansehen. Ihr Beitrag zur ukrainischen Militärgewalt ist ein Beitrag zu meiner Ruhe, sodass ich mehr Zeit damit verbringen kann, das Projekt zu entwickeln.
Danke schön.
MIT © Serhii Potapov