O NATYPE é uma macro Proc que permite adicionar restrições extras, como higienização e validação ao padrão regular do NewType. O código gerado torna impossível instanciar um valor sem passar os cheques. Funciona dessa maneira mesmo com a desserialização 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 ) ,
) ;Para mais informações, veja:
Os desinfetantes disponíveis, validadores e características deriváveis são determinados pelo tipo interno, que se enquadra nas seguintes categorias:
u8 , u16 , u32 , u64 , u128 , i8 , i16 , i32 , i64 , i128 , usize , isize )f32 , f64 ) No momento, o tipo interno da string suporta apenas o tipo String (de propriedade).
| Desinfetante | Descrição | Exemplo |
|---|---|---|
trim | Remove os espaços em branco que lideram e à direita | trim |
lowercase | Converte a string em minúsculas | lowercase |
uppercase | Converte a string em maiúsculas | uppercase |
with | Desinfetante personalizado. Uma função ou fechamento que recebe String e retorna String | with = |mut s: String| ( s.truncate(5); s ) |
| Validador | Descrição | Variante de erro | Exemplo |
|---|---|---|---|
len_char_min | Min Comprimento da corda (em chars, não bytes) | LenCharMinViolated | len_char_min = 5 |
len_char_max | Comprimento máximo da corda (em chars, não bytes) | LenCharMaxViolated | len_char_max = 255 |
not_empty | Rejeita uma corda vazia | NotEmptyViolated | not_empty |
regex | Valida o formato com um regex. Requer recurso regex . | RegexViolated | regex = "^[0-9]{7}$" ou regex = ID_REGEX |
predicate | Validador personalizado. Uma função ou fechamento que &str e bool | PredicateViolated | predicate = |s: &str| s.contains('@') |
with | Validador personalizado com um erro personalizado | N / D | (Veja o exemplo abaixo) |
Requisitos:
regex do nutype está ativado.regex como uma dependência.Existem várias maneiras de usar o Regex.
Uma expressão regular pode ser definida diretamente:
# [ nutype ( validate ( regex = "^[0-9]{3}-[0-9]{3}$" ) ) ]
pub struct PhoneNumber ( String ) ; ou pode ser definido com 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 pode ser definido com 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 ) ; As características a seguir podem ser derivadas para um tipo baseado em cordas: Debug , Clone , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , From , TryFrom , Into Hash , Borrow , Display , Default , Serialize , Deserialize .
Os tipos internos inteiros são: u8 , u16 , u32 , u64 , u128 , i8 , i16 , i32 , i64 , i128 , usize , isize .
| Desinfetante | Descrição | Exemplo |
|---|---|---|
with | Desinfetante personalizado. | with = |raw| raw.clamp(0, 100) |
| Validador | Descrição | Variante de erro | Exemplo |
|---|---|---|---|
less | Limite superior exclusivo | LessViolated | less = 100 |
less_or_equal | Inclusivo limite superior | LessOrEqualViolated | less_or_equal = 99 |
greater | Limite inferior exclusivo | GreaterViolated | greater = 17 |
greater_or_equal | Inclusivo limite inferior | GreaterOrEqualViolated | greater_or_equal = 18 |
predicate | Predicado personalizado | PredicateViolated | predicate = |num| num % 2 == 0 |
with | Validador personalizado com um erro personalizado | N / D | (Veja o exemplo abaixo) |
As características a seguir podem ser derivadas para um tipo com base inteira: Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , Into , From , TryFrom , Hash , Borrow , Display , Default , Serialize , Deserialize .
Os tipos internos de flutuação são: f32 , f64 .
| Desinfetante | Descrição | Exemplo |
|---|---|---|
with | Desinfetante personalizado. | with = |val| val.clamp(0.0, 100.0) |
| Validador | Descrição | Variante de erro | Exemplo |
|---|---|---|---|
less | Limite superior exclusivo | LessViolated | less = 100.0 |
less_or_equal | Inclusivo limite superior | LessOrEqualViolated | less_or_equal = 100.0 |
greater | Limite inferior exclusivo | GreaterViolated | greater = 0.0 |
greater_or_equal | Inclusivo limite inferior | GreaterOrEqualViolated | greater_or_equal = 0.0 |
finite | Verifique contra Nan e Infinity | FiniteViolated | finite |
predicate | Predicado personalizado | PredicateViolated | predicate = |val| val != 50.0 |
with | Validador personalizado com um erro personalizado | N / D | (Veja o exemplo abaixo) |
As características a seguir podem ser derivadas para um tipo baseado em flutuação: Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , Into , From , TryFrom , Hash , Borrow , Display , Default , Serialize , Deserialize .
Também é possível derivar Eq e Ord se as regras de validação garantirem que NaN for excluída. Isso pode ser feito se aplicando pela validação finite . Por exemplo:
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
struct Size ( f64 ) ; Para qualquer outro tipo, é possível definir desinfetantes personalizados with e vorações personalizadas com 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 > ) ;Também é possível usar genéricos:
# [ 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 ) ; Você pode definir desinfetantes personalizados usando a opção with opção. Um desinfetante personalizado é uma função ou fechamento que recebe um valor de um tipo interno com propriedade e retorna um valor higienizado.
Por exemplo, este
# [ nutype ( sanitize ( with = new_to_old ) ) ]
pub struct CityName ( String ) ;
fn new_to_old ( s : String ) -> String {
s . replace ( "New" , "Old" )
}é igual ao seguinte:
# [ nutype ( sanitize ( with = |s| s . replace ( "New" , "Old" ) ) ) ]
pub struct CityName ( String ) ;E funciona da mesma maneira:
let city = CityName :: new ( "New York" ) ;
assert_eq ! ( city . into_inner ( ) , "Old York" ) ; De maneira semelhante, é possível definir validadores personalizados, mas uma função de validação recebe uma referência e retorna bool . Pense nisso como um predicado.
# [ 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 )
} Para definir seu próprio tipo de erro e implementar a lógica de validação personalizada, você pode combinar os atributos with e 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 ) ; É importante garantir que o tipo especificado no atributo error corresponda ao tipo de erro retornado pela função de validação.
Default # [ nutype (
derive ( Default ) ,
default = "Anonymous" ,
) ]
pub struct Name ( String ) ;Eq e Ord em tipos de flutuação Com o NATYPE, é possível derivar Eq e Ord se houver um conjunto de validação finite . A validação finite garante que o valor válido exclua NaN .
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
pub struct Weight ( f64 ) ; Está desanimado, mas é possível ignorar as restrições, permitindo o recurso Crate new_unchecked e marcando um tipo com 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 - Permite derivar de arbitrary::Arbitrary .new_unchecked - Ativa a geração de função não segura ::new_unchecked() .regex - permite usar regex = validação em tipos baseados em string. Nota: sua caixa também deve ter explicitamente regex dentro de suas dependências.serde - Integrações com serde Crate. Permite derivar Serialize e Deserialize características.schemars08 - Permite derivar a característica JsonSchema de Crate de esquemas. Observe que, no momento, as regras de validação não são respeitadas.std - ativado por padrão. Use default-features = false para Desativar. Hoje moro em Berlim, tenho o luxo de viver uma vida fisicamente segura. Mas eu sou ucraniano. Os primeiros 25 anos da minha vida que passei em Kharkiv, a segunda maior cidade da Ucrânia, a 60 km da fronteira com a Rússia. Hoje, cerca de um terço da minha cidade natal é destruído pelos russos. Meus pais, meus parentes e meus amigos tiveram que sobreviver à artilharia e ao ataque aéreo, vivendo por mais de um mês em porões.
Alguns deles conseguiram evacuar a UE. Alguns outros estão tentando viver "vidas normais" em Kharkiv, fazendo tarefas diárias. E alguns estão na linha de frente agora, arriscando suas vidas a cada segundo para proteger o resto.
Encorajo você a doar à Charity Foundation of Serhiy Prytula. Basta escolher o projeto que você gosta e doar. Esta é uma das fundações mais conhecidas, você pode assistir a um pequeno documentário sobre isso. Sua contribuição para a força militar ucraniana é uma contribuição para minha calma, para que eu possa gastar mais tempo desenvolvendo o projeto.
Obrigado.
MIT © Serhii Potapov