#validation #user-input #input-validation #user #rocket #serde #rocket-framework

无 std validators

这个库旨在验证和建模用户输入。该包包括模型、函数、特质、错误和其他依赖项。

102 个版本

0.25.3 2023 年 12 月 19 日
0.24.4 2023 年 9 月 9 日
0.24.3 2022 年 11 月 4 日
0.24.2 2022 年 5 月 10 日
0.18.8 2018 年 11 月 14 日

#58解析器实现

Download history 289/week @ 2024-04-29 314/week @ 2024-05-06 413/week @ 2024-05-13 534/week @ 2024-05-20 592/week @ 2024-05-27 436/week @ 2024-06-03 272/week @ 2024-06-10 374/week @ 2024-06-17 411/week @ 2024-06-24 419/week @ 2024-07-01 303/week @ 2024-07-08 343/week @ 2024-07-15 260/week @ 2024-07-22 802/week @ 2024-07-29 255/week @ 2024-08-05 442/week @ 2024-08-12

1,784 每月下载量
用于 13 个包 (9 直接)

MIT 许可证

175KB
3.5K SLoC

Validators

CI

这个库旨在验证和建模用户输入。该包包括模型、函数、特质、错误和其他依赖项。

功能

默认情况下,此包支持的每个验证器都将启用。您可以通过关闭默认功能来禁用所有功能,并通过将它们显式添加到 features 中来仅启用您想要使用的验证器。

例如,

[dependencies.validators]
version = "*"
features = ["base64", "url", "uuid"]
default-features = false

某些验证器不需要使用 std 库。但是,如果需要,您可以显式启用 std 功能。

通过启用 serderocket 功能,该库可以支持 Serde 框架和 Rocket 框架。

Validators

use validators::prelude::*;

/*
#[derive(Validator)]
#[validator(validator_name)]
DEFINE_YOUR_STRUCT_HERE
*/

当您将 #[validator(validator_name)] 属性应用于您的结构体时,validators::traits 模块中的一个或多个特质将被自动实现。这些特质可以用于验证和反序列化。

用作验证器的结构体应具有与其验证器类型和关联参数对应的特定组件。例如,一个 base32 验证器必须是一个 struct(String),而一个 base32_decoded 验证器应该是一个 struct(Vec<u8>)

属性 `#[validator(validator_name)]` 不能应用于任何结构体或枚举中的字段。选择使用过程宏来定义验证器(即结构体)而不是为每个配置提供内置的结构体,其动机在于消除可配置验证的运行时开销,并提高编译速度。

base32

use validators::prelude::*;

#[derive(Validator)]
#[validator(base32(padding(Must)))]
pub struct Base32WithPadding(String);

assert!(Base32WithPadding::parse_string("GEZDGNBVGY3TQOI=").is_ok());
assert!(Base32WithPadding::parse_string("GEZDGNBVGY3TQOI").is_err());
assert_eq!("GEZDGNBVGY3TQOI=", Base32WithPadding::parse_string("GEZDGNBVGY3TQOI=").unwrap().0);
  • 特性:ValidateStringValidateBytes
  • 默认情况下,padding = Allow

base32_decoded

use validators::prelude::*;

#[derive(Validator)]
#[validator(base32_decoded(padding(Must)))]
pub struct Base32WithPaddingDecoded(Vec<u8>);

assert!(Base32WithPaddingDecoded::parse_string("GEZDGNBVGY3TQOI=").is_ok());
assert!(Base32WithPaddingDecoded::parse_string("GEZDGNBVGY3TQOI").is_err());
assert_eq!(b"123456789", Base32WithPaddingDecoded::parse_string("GEZDGNBVGY3TQOI=").unwrap().0.as_slice());
  • 特性:ValidateStringValidateBytesCollectionLength
  • 默认情况下,padding = Allow

base64

use validators::prelude::*;

#[derive(Validator)]
#[validator(base64(padding(Must)))]
pub struct Base64WithPadding(String);

assert!(Base64WithPadding::parse_string("MTIzNDU2Nzg5MA==").is_ok());
assert!(Base64WithPadding::parse_string("MTIzNDU2Nzg5MA").is_err());
assert_eq!("MTIzNDU2Nzg5MA==", Base64WithPadding::parse_string("MTIzNDU2Nzg5MA==").unwrap().0);
  • 特性:ValidateStringValidateBytes
  • 默认情况下,padding = Allow

base64_decoded

use validators::prelude::*;

#[derive(Validator)]
#[validator(base64_decoded(padding(Must)))]
pub struct Base64WithPaddingDecoded(Vec<u8>);

assert!(Base64WithPaddingDecoded::parse_string("MTIzNDU2Nzg5MA==").is_ok());
assert!(Base64WithPaddingDecoded::parse_string("MTIzNDU2Nzg5MA").is_err());
assert_eq!(b"1234567890", Base64WithPaddingDecoded::parse_string("MTIzNDU2Nzg5MA==").unwrap().0.as_slice());
  • 特性:ValidateStringValidateBytesCollectionLength
  • 默认情况下,padding = Allow

base64_url

use validators::prelude::*;

#[derive(Validator)]
#[validator(base64_url(padding(Disallow)))]
pub struct Base64UrlWithoutPadding(String);

assert!(Base64UrlWithoutPadding::parse_string("5LmN5pqW6YKE5a-S5pmC5YCZ").is_ok());
assert!(Base64UrlWithoutPadding::parse_string("5LmN5pqW6YKE5a+S5pmC5YCZ").is_err());
assert_eq!("5LmN5pqW6YKE5a-S5pmC5YCZ", Base64UrlWithoutPadding::parse_string("5LmN5pqW6YKE5a-S5pmC5YCZ").unwrap().0);
  • 特性:ValidateStringValidateBytes
  • 默认情况下,padding = Allow

base64_url_decoded

use validators::prelude::*;

#[derive(Validator)]
#[validator(base64_url_decoded(padding(Disallow)))]
pub struct Base64UrlWithoutPaddingDecoded(Vec<u8>);

assert!(Base64UrlWithoutPaddingDecoded::parse_string("5LmN5pqW6YKE5a-S5pmC5YCZ").is_ok());
assert!(Base64UrlWithoutPaddingDecoded::parse_string("5LmN5pqW6YKE5a+S5pmC5YCZ").is_err());
assert_eq!("乍暖還寒時候".as_bytes(), Base64UrlWithoutPaddingDecoded::parse_string("5LmN5pqW6YKE5a-S5pmC5YCZ").unwrap().0.as_slice());
  • 特性:ValidateStringValidateBytesCollectionLength
  • 默认情况下,padding = Allow

bit

use validators::prelude::*;
use validators::byte_unit::Bit;

#[derive(Validator)]
#[validator(bit(range(min = 1)))]
pub struct AtLeastOneBit(Bit);

assert!(AtLeastOneBit::parse_string("1kb").is_ok());
assert!(AtLeastOneBit::parse_string("0b").is_err());
assert_eq!(1000u64, AtLeastOneBit::parse_string("1kb").unwrap().0);
  • 特性:ValidateStringValidateUnsignedInteger
  • 默认情况下,范围不受限制

boolean

use validators::prelude::*;

#[derive(Validator)]
#[validator(boolean)]
pub struct Boolean(bool);

assert_eq!(true, Boolean::parse_str("true").unwrap().0);
assert_eq!(false, Boolean::parse_str("f").unwrap().0);
assert_eq!(true, Boolean::parse_str("y").unwrap().0);
assert_eq!(false, Boolean::parse_str("no").unwrap().0);
assert_eq!(true, Boolean::parse_str("on").unwrap().0);
assert_eq!(false, Boolean::parse_str("off").unwrap().0);
assert_eq!(true, Boolean::parse_str("1").unwrap().0);

assert_eq!(true, Boolean::parse_char('t').unwrap().0);
assert_eq!(false, Boolean::parse_char('0').unwrap().0);

assert_eq!(true, Boolean::parse_isize(1).unwrap().0);
  • 特性:ValidateStringValidateCharValidateSignedIntegerValidateUnsignedIntegerValidateBoolean

byte

use validators::prelude::*;
use validators::byte_unit::Byte;

#[derive(Validator)]
#[validator(byte(range(min = 1), ignore_case = false))]
pub struct AtLeastOneByte(Byte);

assert!(AtLeastOneByte::parse_string("1KB").is_ok());
assert!(AtLeastOneByte::parse_string("0B").is_err());
assert_eq!(1000u64, AtLeastOneByte::parse_string("1KB").unwrap().0);
  • 特性:ValidateStringValidateUnsignedInteger
  • 默认情况下,范围不受限制,ignore_case = true

domain

use validators::prelude::*;

#[derive(Validator)]
#[validator(domain(ipv4(Allow), local(Allow), port(Disallow), at_least_two_labels(Allow)))]
pub struct DomainWithoutPort(pub String);

assert!(DomainWithoutPort::parse_string("example.com").is_ok());
assert!(DomainWithoutPort::parse_string("example.com.").is_ok());
assert_eq!("xn--fiq228c.com", DomainWithoutPort::parse_string("中文.com").unwrap().0);

#[derive(Validator)]
#[validator(domain(ipv4(Allow), local(Allow), port(Allow), at_least_two_labels(Allow)))]
pub struct DomainAllowPort {
    pub domain: String,
    port: Option<u16>,
}

assert_eq!(Some(8080), DomainAllowPort::parse_string("example.com:8080").unwrap().port);
  • 特性:ValidateStringQualifyDomainToUriAuthorityString
  • 默认情况下,ipv4 = Allow, local = Allow, port = Allow, at_least_two_labels = Allow

email

use validators::prelude::*;
use validators::models::Host;

#[derive(Validator)]
#[validator(email(comment(Allow), ip(Allow), local(Allow), at_least_two_labels(Allow), non_ascii(Allow)))]
pub struct EmailAllowComment {
    pub local_part: String,
    pub need_quoted: bool,
    pub domain_part: Host,
    pub comment_before_local_part: Option<String>,
    pub comment_after_local_part: Option<String>,
    pub comment_before_domain_part: Option<String>,
    pub comment_after_domain_part: Option<String>,
}

assert!(EmailAllowComment::parse_string("(john)[email protected]").is_ok());

#[derive(Validator)]
#[validator(email(comment(Disallow), ip(Allow), local(Allow), at_least_two_labels(Allow), non_ascii(Allow)))]
pub struct EmailWithoutComment {
    pub local_part: String,
    pub need_quoted: bool,
    pub domain_part: Host,
}

assert!(EmailWithoutComment::parse_string("(john)[email protected]").is_err());
  • 特性:ValidateStringToEmailString
  • 默认情况下,comment = Allow, ip = Allow, local = Allow, at_least_two_labels = Allow, non_ascii = Allow

host

use validators::prelude::*;
use validators::models::Host;

#[derive(Validator)]
#[validator(host(local(Allow), port(Allow), at_least_two_labels(Must)))]
pub struct HostMustAtLeastTwoLabelsAllowPort {
    pub host: Host,
    pub port: Option<u16>,
    pub is_local: bool,
}

assert!(HostMustAtLeastTwoLabelsAllowPort::parse_string("example.com:8000").is_ok());
assert!(HostMustAtLeastTwoLabelsAllowPort::parse_string("example.com.").is_err());
assert!(HostMustAtLeastTwoLabelsAllowPort::parse_string("example").is_err());
  • 特性:ValidateStringToUriAuthorityString
  • 默认情况下,local = Allow, port = Allow, at_least_two_labels = Allow

http_url

use validators::prelude::*;
use validators::url::Url;

#[derive(Validator)]
#[validator(http_url(local(Allow)))]
pub struct HttpURL {
    url: Url,
    is_https: bool,
}

assert!(HttpURL::parse_string("https://example.org/").is_ok());
assert!(HttpURL::parse_string("http://example.org/").is_ok());
assert!(HttpURL::parse_string("ftp://example.org/").is_err());
  • 特性:ValidateString
  • 默认情况下,local = Allow

http_ftp_url

use validators::prelude::*;
use validators::models::Protocol;
use validators::url::Url;

#[derive(Validator)]
#[validator(http_ftp_url(local(Allow)))]
pub struct HttpFtpURL {
    url: Url,
    protocol: Protocol,
}

assert!(HttpFtpURL::parse_string("https://example.org/").is_ok());
assert!(HttpFtpURL::parse_string("http://example.org/").is_ok());
assert!(HttpFtpURL::parse_string("ftp://example.org/").is_ok());
  • 特性:ValidateString
  • 默认情况下,local = Allow

ip

use std::net::IpAddr;

use validators::prelude::*;

#[derive(Validator)]
#[validator(ip(local(Allow), port(Allow)))]
pub struct IpAllowPort {
    pub ip: IpAddr,
    pub port: Option<u16>,
}

assert!(IpAllowPort::parse_string("127.0.0.1").is_ok());
assert!(IpAllowPort::parse_string("[::ffff:c000:0280]:8000").is_ok());
  • 特性:ValidateStringToUriAuthorityString
  • 默认情况下,local = Allow, port = Allow

ipv4

use std::net::Ipv4Addr;

use validators::prelude::*;

#[derive(Validator)]
#[validator(ipv4(local(Allow), port(Allow)))]
pub struct Ipv4AllowPort {
    pub ipv4: Ipv4Addr,
    pub port: Option<u16>,
}

assert!(Ipv4AllowPort::parse_string("127.0.0.1").is_ok());
  • 特性:ValidateStringToUriAuthorityString
  • 默认情况下,local = Allow, port = Allow

ipv6

use std::net::Ipv6Addr;

use validators::prelude::*;

#[derive(Validator)]
#[validator(ipv6(local(Allow), port(Allow)))]
pub struct Ipv6AllowPort {
    pub ipv6: Ipv6Addr,
    pub port: Option<u16>,
}

assert!(Ipv6AllowPort::parse_string("::ffff:c000:0280").is_ok());
assert!(Ipv6AllowPort::parse_string("[::ffff:c000:0280]").is_ok());
  • 特性:ValidateStringToUriAuthorityString
  • 默认情况下,local = Allow, port = Allow

json

use validators::prelude::*;

#[derive(Validator)]
#[validator(json)]
pub struct JSONString(pub String);

#[derive(Validator)]
#[validator(json)]
pub struct JSONNumber(pub f64);

#[derive(Validator)]
#[validator(json)]
pub struct JSONBoolean(pub bool);

assert!(JSONString::parse_string("123").is_err());
assert!(JSONString::parse_string("\"123\"").is_ok());
assert!(JSONNumber::parse_u64(123).is_ok());
assert!(JSONBoolean::parse_bool(false).is_ok());
  • 特性:ValidateStringValidateSignedIntegerValidateUnsignedIntegerValidateNumberValidateBooleanValidateJsonValue

length

use validators::prelude::*;

#[derive(Validator)]
#[validator(length(min = 1, max = 3))]
pub struct NonEmptyNotTooLongVec(pub Vec<u8>);

assert!(NonEmptyNotTooLongVec::parse_collection(vec![]).is_err());
assert!(NonEmptyNotTooLongVec::parse_collection(vec![0]).is_ok());
assert!(NonEmptyNotTooLongVec::parse_collection(vec![0, 1, 2, 3]).is_err());
  • 特性:ValidateLengthCollectionLength
  • 默认情况下,长度不受限制

line

use validators::prelude::*;

#[derive(Validator)]
#[validator(line(char_length(trimmed_min = 1, min = 1, max = 1000)))] // `byte_length` can also be used
pub struct LineNotAllowEmpty(pub String);

assert!(LineNotAllowEmpty::parse_string("123").is_ok());
assert!(LineNotAllowEmpty::parse_string("123\0").is_err());
assert!(LineNotAllowEmpty::parse_string("123\n456").is_err());
assert!(LineNotAllowEmpty::parse_string("   ").is_err());
  • 特性:ValidateLength
  • 默认情况下,长度不受限制

mac_address

use validators::prelude::*;

#[derive(Validator)]
#[validator(mac_address(case(Upper), separator(Allow(b':'))))]
pub struct MacAddress(pub u64);

assert!(MacAddress::parse_string("080027B246C3").is_ok());
assert!(MacAddress::parse_string("08:00:27:B2:46:C3").is_ok());
  • 特性:ValidateStringToMacAddressString
  • 默认情况下,case = Any, separator(Allow(b':')

数字

use validators::prelude::*;

#[derive(Validator)]
#[validator(number(nan(Disallow), range(Unlimited)))]
pub struct Double(pub f64);

assert!(Double::parse_string("123.456").is_ok());
assert!(Double::parse_string("NaN").is_err());
assert!(Double::parse_f32(123.4).is_ok());

#[derive(Validator)]
#[validator(number(nan(Allow), range(Inside(min = 0, max = 1.0))))]
pub struct SinglePercentage(pub f32);

assert!(SinglePercentage::parse_string("0").is_ok());
assert!(SinglePercentage::parse_string("1").is_ok());
assert!(SinglePercentage::parse_string("1.1").is_err());
assert!(SinglePercentage::parse_string("NaN").is_ok());
  • 特性:ValidateStringValidateNumber
  • 默认情况下,nan = Allow, range(Unlimited)

电话

use std::collections::HashMap;

use validators::prelude::*;
use validators_prelude::phonenumber::PhoneNumber;

#[derive(Validator)]
#[validator(phone)]
pub struct InternationalPhone(pub phonenumber::PhoneNumber);

#[derive(Validator)]
#[validator(phone(countries(TW)))]
pub struct TWPhone(pub phonenumber::PhoneNumber);

#[derive(Validator)]
#[validator(phone(countries(TW, US)))]
pub struct TWorUSPhone(
    pub HashMap<phonenumber::country::Id, phonenumber::PhoneNumber>,
);

assert!(InternationalPhone::parse_string("+886912345678").is_ok());
assert!(InternationalPhone::parse_string("0912345678").is_err());
assert!(InternationalPhone::parse_string("+14155552671").is_ok());

assert!(TWPhone::parse_string("+886912345678").is_ok());
assert!(TWPhone::parse_string("0912345678").is_ok());
assert!(TWPhone::parse_string("+14155552671").is_err());

assert!(TWorUSPhone::parse_string("+886912345678").is_ok());
assert!(TWorUSPhone::parse_string("0912345678").is_ok());
assert!(TWorUSPhone::parse_string("+14155552671").is_ok());
  • 特性:ValidateString
  • 默认情况下,国家不限

正则表达式

use validators::prelude::*;
use validators_prelude::regex::Regex;

use once_cell::sync::Lazy;

static RE_NON_ZERO_NUMBERS: Lazy<Regex> = Lazy::new(|| {
    Regex::new("^[1-9]+$").unwrap()
});

static RE_POKER: Lazy<Regex> = Lazy::new(|| {
    Regex::new("^([AJQK1-9]|10)$").unwrap()
});

#[derive(Validator)]
#[validator(regex(regex("^[0-9a-fA-F]+$")))]
pub struct Hex(pub String); // this doesn't cache the `Regex` instance

#[derive(Validator)]
#[validator(regex(regex(RE_NON_ZERO_NUMBERS)))]
pub struct NonZeroNumbers(pub String);

#[derive(Validator)]
#[validator(regex(regex(RE_POKER)))]
pub struct Poker(pub String);

assert!(Hex::parse_string("1Ab").is_ok());
assert!(Hex::parse_string("1AG").is_err());

assert!(NonZeroNumbers::parse_string("12345").is_ok());
assert!(NonZeroNumbers::parse_string("012345").is_err());

assert!(Poker::parse_string("1").is_ok());
assert!(Poker::parse_string("10").is_ok());
assert!(Poker::parse_string("J").is_ok());
assert!(Poker::parse_string("0").is_err());
  • 特性:ValidateString
  • regex参数必须设置为字符串字面量或表达式

语义版本

use validators::prelude::*;
use validators_prelude::semver::Version;

#[derive(Validator)]
#[validator(semver)]
pub struct SemVer(Version);

assert!(SemVer::parse_string("0.0.0").is_ok());
assert!(SemVer::parse_string("0.0.0-beta.1").is_ok());
  • 特性:ValidateString

semver_req

use validators::prelude::*;
use validators_prelude::semver::VersionReq;

#[derive(Validator)]
#[validator(semver_req)]
pub struct SemVerReq(VersionReq);

assert!(SemVerReq::parse_string("0.0.0").is_ok());
assert!(SemVerReq::parse_string(">= 0.4").is_ok());
  • 特性:ValidateString

有符号整数

use validators::prelude::*;

#[derive(Validator)]
#[validator(signed_integer(range(Inside(min = -1, max = 100))))]
pub struct Score(i8);

assert!(Score::parse_string("0").is_ok());
assert!(Score::parse_string("-2").is_err());
assert!(Score::parse_i8(4).is_ok());

#[derive(Validator)]
#[validator(signed_integer(range(Outside(min = 0, max = 0))))]
pub struct NonZeroShort(i16);

assert!(NonZeroShort::parse_i8(4).is_ok());
assert!(NonZeroShort::parse_i8(-4).is_ok());
assert!(NonZeroShort::parse_i8(0).is_err());
  • 特性:ValidateStringValidateSignedInteger
  • 默认情况下,range(Unlimited)

文本

use validators::prelude::*;

#[derive(Validator)]
#[validator(text(char_length(trimmed_min = 1, min = 1, max = 1000)))] // `byte_length` can also be used
pub struct TextNotAllowEmpty(pub String);

assert!(TextNotAllowEmpty::parse_string("123").is_ok());
assert!(TextNotAllowEmpty::parse_string("123\0").is_err());
assert!(TextNotAllowEmpty::parse_string("123\n456").is_ok());
assert!(TextNotAllowEmpty::parse_string("   ").is_err());
  • 特性:ValidateLength
  • 默认情况下,长度不受限制

无符号整数

use validators::prelude::*;

#[derive(Validator)]
#[validator(unsigned_integer(range(Inside(min = 1, max = 100))))]
pub struct Count(u8);

assert!(Count::parse_string("5").is_ok());
assert!(Count::parse_string("0").is_err());
assert!(Count::parse_u8(4).is_ok());
  • 特性:ValidateStringValidateUnsignedInteger
  • 默认情况下,range(Unlimited)

URL

use validators::prelude::*;
use validators_prelude::url::Url;

#[derive(Validator)]
#[validator(url)]
pub struct URL(pub Url);

assert!(URL::parse_string("https://example.org/").is_ok());
assert!(URL::parse_string("https:example.org").is_ok());
assert!(URL::parse_string("example:").is_ok());
  • 特性:ValidateString

UUID

use validators::prelude::*;

#[derive(Validator)]
#[validator(uuid(case(Upper), separator(Allow(b'-'))))]
pub struct UUID(pub u128);

assert!(UUID::parse_string("A866664AF9D34DDE89CB182015FA4F41").is_ok());
assert!(UUID::parse_string("A866664A-F9D3-4DDE-89CB-182015FA4F41").is_ok());
  • 特性:ValidateStringToUuidString
  • 默认情况下,case = Any, separator(Allow(b'-')

验证器::结果

当将您的验证器类型合并到其他类型时,您可能希望获取验证器提供的原始错误实例。

例如,当使用Rocket框架中的number验证器与#[derive(FromForm)]属性时,

use rocket::{FromForm, get};
use validators::prelude::*;

#[derive(Debug, Validator)]
#[validator(number(range(Outside(max = 0))))]
pub struct NonZeroNumber(f64);

#[derive(Debug, FromForm)]
struct User {
    id:   i32,
    number: NonZeroNumber,
}

#[get("/?<user..>")]
fn index(user: User) -> String {
    format!("{:?}", user)
}

您可能希望允许User实例的数字字段失败。您可以按如下修改代码

use rocket::{FromForm, get};

use validators::prelude::*;

#[derive(Debug, Validator)]
#[validator(number(range(Outside(max = 0))))]
pub struct NonZeroNumber(f64);

#[derive(Debug, FromForm)]
struct User {
    id:   i32,
    // number: Result<NonZeroNumber, validators::errors::NumberError>, // compile error
    number: validators::Result<NonZeroNumber, validators::errors::NumberError>,
}

#[get("/?<user..>")]
fn index(user: User) -> String {
    format!("{:?}", user)
}

Crates.io

https://crates.io/crates/validators

文档

https://docs.rs/validators

许可证

MIT

依赖

~0–36MB
~496K SLoC