Nutype - это макрос Proc, который позволяет добавлять дополнительные ограничения, такие как дезинфекция и проверка, к обычному рисунку Newtype. Сгенерированный код делает невозможным создание значения, не проходя проверки. Это работает так даже с 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 ) ,
) ;Для получения дополнительной информации, пожалуйста, посмотрите:
Доступные дезинфицирующие средства, валидаторы и производные черты определяются внутренним типом, который подпадает от следующих категорий:
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 | Мин длина струны (в частях, а не байтах) | LenCharMinViolated | len_char_min = 5 |
len_char_max | Максимальная длина строки (в частях, а не байтах) | LenCharMaxViolated | len_char_max = 255 |
not_empty | Отвергает пустую строку | NotEmptyViolated | not_empty |
regex | Проверяет формат с помощью корпорации. Требуется функция regex . | RegexViolated | regex = "^[0-9]{7}$" или regex = ID_REGEX |
predicate | Пользовательский валидатор. Функция или закрытие, которое &str и возвращает bool | PredicateViolated | predicate = |s: &str| s.contains('@') |
with | Пользовательский валидатор с пользовательской ошибкой | N/a | (См. Пример ниже) |
Требования:
regex nutype включена.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 ) ; Следующие признаки могут быть получены для типа на основе строки: Debug , Clone , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , From , TryFrom , Into Hash , Display Borrow Default , Serialize , 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 | (См. Пример ниже) |
Следующие черты могут быть получены для целочисленного типа: Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , FromStr , AsRef , Deref , Into , From , TryFrom , Hash , Display Borrow Default , Serialize , 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 | Проверьте Нэн и Бесконечность | 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 , From , Borrow , Display , Default , Deserialize .
Также можно получить Eq и Ord , если правила проверки гарантируют, что NaN исключена. Это может быть сделано применять с помощью finite проверки. Например:
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
struct Size ( f64 ) ; Для любого другого типа можно определить пользовательские дезинфицирующие средства с with и пользовательских подтверждений с 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 > ) ;Также возможно использовать дженерики:
# [ 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 . Пользовательское дезинфицирующее средство - это функция или закрытие, которое получает значение внутреннего типа с владением и возвращает дезинфицированную стоимость.
Например, этот
# [ 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 можно получить Eq и Ord если есть набор finite проверки. finite валидация гарантирует, что допустимое значение исключает NaN .
# [ nutype (
validate ( finite ) ,
derive ( PartialEq , Eq , PartialOrd , Ord ) ,
) ]
pub struct Weight ( f64 ) ; Это обескуражено, но можно обойти ограничения, включив функцию new_unchecked Crate и отметив тип 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 - включает генерацию небезопасной функции ::new_unchecked() .regex - позволяет использовать regex = валидация на типах на основе строк. Примечание: ваш ящик также должен явно иметь regex в рамках своей зависимости.serde - Интеграция с serde ящиком. Позволяет получить Serialize и Deserialize черты.schemars08 - Позволяет получить черту JsonSchema схемы ящика. Обратите внимание, что на данный момент правила проверки не уважаются.std - включен по умолчанию. Используйте default-features = false , чтобы отключить. Сегодня я живу в Берлине, у меня есть роскошь жить физически безопасной жизнью. Но я украинский. Первые 25 лет моей жизни я провел в Харкиве, втором по величине городе в Украине, в 60 км от границы с Россией. Сегодня около трети моего родного города разрушается русскими. Мои родители, мои родственники и мои друзья должны были пережить артиллерию и воздушную атаку, живя более месяца в подвалах.
Некоторым из них удалось эвакуироваться в ЕС. Некоторые другие пытаются жить «нормальной жизнью» в Харкиве, выполняя там ежедневные обязанности. А некоторые сейчас на линии фронта, рискуя каждую секунду, чтобы защитить остальных.
Я призываю вас пожертвовать в благотворительный фонд Серхий Прайтула. Просто выберите проект, который вам нравится, и пожертвуйте. Это одна из самых известных фондов, вы можете посмотреть небольшой документальный фильм об этом. Ваш вклад в украинскую военную силу является вкладом в мое спокойствие, поэтому я могу потратить больше времени на разработку проекта.
Спасибо.
MIT © Serhii Potapov