Nutypeは、通常のNewTypeパターンに消毒や検証などの追加の制約を追加できるProcマクロです。生成されたコードにより、チェックを渡さずに値をインスタンス化することは不可能になります。 serde Deserializationでもこのように機能します。
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 ) ,
) ;詳細については、ご覧ください。
利用可能な消毒剤、バリデーター、および派生可能な特性は、次のカテゴリに分類される内部タイプによって決定されます。
u8 、 u16 、 u32 、 u64 、 u128 、 i8 、 i16 、 i32 、 i64 、 i128 、 usize 、 isize )f32 、 f64 )現時点では、文字列内側のタイプは、 String (所有)タイプのみをサポートしています。
| 消毒剤 | 説明 | 例 |
|---|---|---|
trim | リーディングおよびトレーリングホワイトスペースを削除します | trim |
lowercase | 文字列を小文字に変換します | lowercase |
uppercase | 文字列を大文字に変換します | uppercase |
with | カスタム消毒剤。 Stringを受信してStringを返す関数または閉鎖 | with = |mut s: String| ( s.truncate(5); s ) |
| バリデーター | 説明 | エラーバリアント | 例 |
|---|---|---|---|
len_char_min | 文字列の最小長さ(バイトではなく、charsで) | LenCharMinViolated | len_char_min = 5 |
len_char_max | 文字列の最大長(バイトではなく、charsで) | LenCharMaxViolated | len_char_max = 255 |
not_empty | 空の文字列を拒否します | NotEmptyViolated | not_empty |
regex | 正規表現でフォーマットを検証します。 regex機能が必要です。 | RegexViolated | regex = "^[0-9]{7}$"またはregex = ID_REGEX |
predicate | カスタムバリーター。 boolを受信して&strを受信して返す機能または閉鎖 | PredicateViolated | predicate = |s: &str| s.contains('@') |
with | カスタムエラーを備えたカスタムバリーター | n/a | (以下の例を参照) |
要件:
nutypeのregex機能が有効になっています。regex明示的に含める必要があります。Regexを使用する方法はいくつかあります。
正規表現は正しい定義できます。
# [ nutype ( validate ( regex = "^[0-9]{3}-[0-9]{3}$" ) ) ]
pub struct PhoneNumber ( String ) ;または、 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 ) ;または、 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 ) ;または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 ) ;次の特性はSerialize文字列ベースのタイプで導き出すことができます: Debug 、 Clone 、 PartialEq 、 Eq 、 PartialOrd 、 Ord 、 FromStr 、 AsRef 、 Deref 、 From 、 TryFrom 、 Into 、 Hash 、 Borrow 、 Display 、 Default 、 Deserialize 。
整数の内部タイプは、 u8 、 u16 、 u32 、 u64 、 u128 、 i8 、 i16 、 i32 、 i64 、 i128 、 usize 、 isize 。
| 消毒剤 | 説明 | 例 |
|---|---|---|
with | カスタム消毒剤。 | with = |raw| raw.clamp(0, 100) |
| バリデーター | 説明 | エラーバリアント | 例 |
|---|---|---|---|
less | 排他的な上限 | LessViolated | less = 100 |
less_or_equal | 包括的上限 | LessOrEqualViolated | less_or_equal = 99 |
greater | 排他的な下限 | GreaterViolated | greater = 17 |
greater_or_equal | 包括的下限 | GreaterOrEqualViolated | greater_or_equal = 18 |
predicate | カスタム述語 | PredicateViolated | predicate = |num| num % 2 == 0 |
with | カスタムエラーを備えたカスタムバリーター | n/a | (以下の例を参照) |
整数ベースのタイプでは、次の特性Serialize導き出すことができます: Debug 、 Clone 、 Copy 、 PartialEq 、 Eq 、 PartialOrd 、 Ord 、 FromStr 、 AsRef 、 Deref 、 Into 、 From 、 TryFrom 、 Hash 、 Borrow 、 Display 、 Default 、 Deserialize 。
フロートの内側タイプは、 f32 、 f64です。
| 消毒剤 | 説明 | 例 |
|---|---|---|
with | カスタム消毒剤。 | with = |val| val.clamp(0.0, 100.0) |
| バリデーター | 説明 | エラーバリアント | 例 |
|---|---|---|---|
less | 排他的な上限 | LessViolated | less = 100.0 |
less_or_equal | 包括的上限 | LessOrEqualViolated | less_or_equal = 100.0 |
greater | 排他的な下限 | GreaterViolated | greater = 0.0 |
greater_or_equal | 包括的下限 | GreaterOrEqualViolated | greater_or_equal = 0.0 |
finite | NanとInfinityに対して確認してください | FiniteViolated | finite |
predicate | カスタム述語 | PredicateViolated | predicate = |val| val != 50.0 |
with | カスタムエラーを備えたカスタムバリーター | n/a | (以下の例を参照) |
次の特性はSerializeフロートベースのタイプの場合に導き出すことができます: Debug 、 Clone 、 Copy 、 PartialEq 、 Eq 、 PartialOrd 、 Ord 、 FromStr 、 AsRef 、 Deref 、 Into 、 From 、 TryFrom 、 Hash 、 Borrow 、 Display 、 Default 、 Deserialize 。
検証ルールがNaNが除外されていることを保証する場合、 EqとOrdを導出することも可能です。これは、 finite検証によって適用できます。例えば:
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
struct Size ( f64 ) ; 他のタイプについては、 predicateをwithしてカスタム検証とカスタム検証を定義することが可能です。
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 > ) ;ジェネリックを使用することも可能です。
# [ 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 ) ; withオプションを使用してカスタム消毒剤を設定できます。カスタム消毒剤は、所有権を持つ内部タイプの値を受け取り、消毒された値を返す機能または閉鎖です。
たとえば、これは1つです
# [ nutype ( sanitize ( with = new_to_old ) ) ]
pub struct CityName ( String ) ;
fn new_to_old ( s : String ) -> String {
s . replace ( "New" , "Old" )
}次のものと同等です。
# [ nutype ( sanitize ( with = |s| s . replace ( "New" , "Old" ) ) ) ]
pub struct CityName ( String ) ;そして同じように動作します:
let city = CityName :: new ( "New York" ) ;
assert_eq ! ( city . into_inner ( ) , "Old York" ) ; 同様に、カスタムバリエーターを定義することは可能ですが、検証関数は参照を受信してboolを返します。それを述語と考えてください。
# [ 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 )
} 独自のエラータイプを定義し、カスタム検証ロジックを実装するには、 withと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 ) ; error属性で指定されたタイプが、検証関数によって返されるエラータイプと一致することを確認することが重要です。
Defaultを導き出します # [ nutype (
derive ( Default ) ,
default = "Anonymous" ,
) ]
pub struct Name ( String ) ;EqとOrdを導き出しますNutypeを使用すると、 finite検証セットがある場合、 EqとOrdを導出することができます。 finite検証により、有効な値がNaNを除外することが保証されます。
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
pub struct Weight ( f64 ) ; それは落胆していますが、 new_unchecked機能を有効にし、 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 - arbitrary::Arbitraryの導出を有効にします。new_unchecked -Unsafe ::new_unchecked()関数の生成を有効にします。regex -regex regex =検証を文字列ベースのタイプで使用できます。注:クレートは、その依存関係の中で明示的にregexを持たなければなりません。serde serde Crateとの統合。 Serializeて脱色特性を導き出し、 Deserializeことができます。schemars08 -Schemars CrateのJsonSchema特性を導き出すことができます。現時点では、検証ルールは尊重されていないことに注意してください。stdデフォルトで有効になっています。 default-features = falseを使用して無効にします。 今日、私はベルリンに住んでいます。肉体的に安全な生活を送る贅沢があります。しかし、私はウクライナ人です。私の人生の最初の25年間は、ロシアとの国境から60 km離れたウクライナで2番目に大きい都市であるカルキフで過ごしました。今日、私の故郷の約3分の1はロシア人によって破壊されています。私の両親、私の親relative、そして私の友人は、地下室で1か月以上住んでいて、砲兵と空中攻撃を生き残らなければなりませんでした。
それらのいくつかは、なんとかEUに避難することができました。他の何人かは、カルキフで「普通の生活」を生きようとしており、そこで毎日職務を遂行しています。そして、いくつかは今最前線にいて、残りを保護するために毎秒彼らの命を危険にさらしています。
Serhiy PrytulaのCharity Foundationに寄付することをお勧めします。好きなプロジェクトを選択して寄付してください。これは最も有名な財団の1つです。それについての小さなドキュメンタリーを見ることができます。ウクライナの軍事力へのあなたの貢献は私の落ち着きへの貢献であるため、プロジェクトの開発にもっと時間を費やすことができます。
ありがとう。
MIT©Serhii Potapov