Add some validators

This commit is contained in:
Sunli 2021-11-16 16:20:40 +08:00
parent 4b1cb058fa
commit 5a1f0ed865
8 changed files with 224 additions and 7 deletions

View File

@ -45,6 +45,12 @@ pub struct Validators {
max_items: Option<usize>,
#[darling(default)]
min_items: Option<usize>,
#[darling(default)]
chars_max_length: Option<usize>,
#[darling(default)]
chars_min_length: Option<usize>,
#[darling(default)]
email: bool,
#[darling(default, multiple)]
custom: Vec<SpannedValue<String>>,
#[darling(default)]
@ -110,6 +116,24 @@ impl Validators {
});
}
if let Some(n) = &self.chars_max_length {
codes.push(quote! {
#crate_name::validators::chars_max_length(#value, #n)
});
}
if let Some(n) = &self.chars_min_length {
codes.push(quote! {
#crate_name::validators::chars_min_length(#value, #n)
});
}
if self.email {
codes.push(quote! {
#crate_name::validators::email(#value)
});
}
for s in &self.custom {
let expr: Expr =
syn::parse_str(s).map_err(|err| Error::new(s.span(), err.to_string()))?;

View File

@ -9,6 +9,9 @@
- **min_items=N** the length of the list cannot be less than `N`.
- **max_length=N** the length of the string cannot be greater than `N`.
- **min_length=N** the length of the string cannot be less than `N`.
- **chars_max_length=N** the count of the unicode chars cannot be greater than `N`.
- **chars_min_length=N** the count of the unicode chars cannot be less than `N`.
- **email** is valid email.
```rust
use async_graphql::*;

View File

@ -2,13 +2,16 @@
`Async-graphql`内置了一些常用的校验器,你可以在对象字段的参数或者`InputObject`的字段上使用它们。
- **maximum=N** 指定数字不能大于N
- **minimum=N** 指定数字不能小于N
- **multiple_of=N** 指定数字必须是N的倍数
- **max_items=N** 指定列表的长度不能大于N
- **min_items=N** 指定列表的长度不能小于N
- **max_length=N** 字符串的长度不能大于N
- **min_length=N** 字符串的长度不能小于N
- **maximum=N** 指定数字不能大于`N`
- **minimum=N** 指定数字不能小于`N`
- **multiple_of=N** 指定数字必须是`N`的倍数
- **max_items=N** 指定列表的长度不能大于`N`
- **min_items=N** 指定列表的长度不能小于`N`
- **max_length=N** 字符串的长度不能大于`N`
- **min_length=N** 字符串的长度不能小于`N`
- **chars_max_length=N** 字符串中unicode字符的的数量不能小于`N`
- **chars_min_length=N** 字符串中unicode字符的的数量不能大于`N`
- **email** 有效的email
```rust
use async_graphql::*;

View File

@ -0,0 +1,29 @@
use crate::{InputType, InputValueError};
pub fn chars_max_length<T: AsRef<str> + InputType>(
value: &T,
len: usize,
) -> Result<(), InputValueError<T>> {
if value.as_ref().chars().count() <= len {
Ok(())
} else {
Err(format!(
"the chars length is {}, must be less than or equal to {}",
value.as_ref().len(),
len
)
.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chars_max_length() {
assert!(chars_max_length(&"你好".to_string(), 3).is_ok());
assert!(chars_max_length(&"你好啊".to_string(), 3).is_ok());
assert!(chars_max_length(&"嗨你好啊".to_string(), 3).is_err());
}
}

View File

@ -0,0 +1,29 @@
use crate::{InputType, InputValueError};
pub fn chars_min_length<T: AsRef<str> + InputType>(
value: &T,
len: usize,
) -> Result<(), InputValueError<T>> {
if value.as_ref().chars().count() >= len {
Ok(())
} else {
Err(format!(
"the chars length is {}, must be greater than or equal to {}",
value.as_ref().len(),
len
)
.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chars_min_length() {
assert!(chars_min_length(&"你好".to_string(), 3).is_err());
assert!(chars_min_length(&"你好啊".to_string(), 3).is_ok());
assert!(chars_min_length(&"嗨你好啊".to_string(), 3).is_ok());
}
}

64
src/validators/email.rs Normal file
View File

@ -0,0 +1,64 @@
use crate::{InputType, InputValueError};
use once_cell::sync::Lazy;
use regex::Regex;
static EMAIL_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new("^(([0-9A-Za-z!#$%&'*+-/=?^_`{|}~&&[^@]]+)|(\"([0-9A-Za-z!#$%&'*+-/=?^_`{|}~ \"(),:;<>@\\[\\\\\\]]+)\"))@").unwrap()
});
pub fn email<T: AsRef<str> + InputType>(value: &T) -> Result<(), InputValueError<T>> {
if EMAIL_RE.is_match(value.as_ref()) {
Ok(())
} else {
Err("invalid email format".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_email() {
let test_cases = [
// Invalid emails
("plainaddress", true),
// ("#@%^%#$@#$@#.com", true),
("@example.com", true),
("Joe Smith <email@example.com>", true),
("email.example.com", true),
// ("email@example@example.com", true),
// (".email@example.com", true),
// ("email.@example.com", true),
// ("email..email@example.com", true),
("あいうえお@example.com", true),
// ("email@example.com (Joe Smith)", true),
// ("email@example", true),
// ("email@-example.com", true),
// ("email@example.web", true),
// ("email@111.222.333.44444", true),
// ("email@example..com", true),
// ("Abc..123@example.com", true),
// Valid Emails
("email@example.com", false),
("firstname.lastname@example.com", false),
("email@subdomain.example.com", false),
("firstname+lastname@example.com", false),
("email@123.123.123.123", false),
("email@[123.123.123.123]", false),
// This returns parsing error
// (r#""email"@example.com"#, false),
("1234567890@example.com", false),
("email@example-one.com", false),
("_______@example.com", false),
("email@example.name", false),
("email@example.museum", false),
("email@example.co.jp", false),
("firstname-lastname@example.com", false),
];
for (s, res) in test_cases {
assert_eq!(email(&s.to_string()).is_err(), res);
}
}
}

View File

@ -1,3 +1,6 @@
mod chars_max_length;
mod chars_min_length;
mod email;
mod max_items;
mod max_length;
mod maximum;
@ -6,6 +9,9 @@ mod min_length;
mod minimum;
mod multiple_of;
pub use chars_max_length::chars_max_length;
pub use chars_min_length::chars_min_length;
pub use email::email;
pub use max_items::max_items;
pub use max_length::max_length;
pub use maximum::maximum;

View File

@ -1,6 +1,65 @@
use async_graphql::*;
use futures_util::{Stream, StreamExt};
#[tokio::test]
pub async fn test_all_validator() {
struct Query;
#[Object]
#[allow(unreachable_code, unused_variables)]
impl Query {
async fn multiple_of(&self, #[graphql(validator(multiple_of = 10))] n: i32) -> i32 {
todo!()
}
async fn maximum(&self, #[graphql(validator(maximum = 10))] n: i32) -> i32 {
todo!()
}
async fn minimum(&self, #[graphql(validator(minimum = 10))] n: i32) -> i32 {
todo!()
}
async fn max_length(&self, #[graphql(validator(max_length = 10))] n: String) -> i32 {
todo!()
}
async fn min_length(&self, #[graphql(validator(min_length = 10))] n: String) -> i32 {
todo!()
}
async fn max_items(&self, #[graphql(validator(max_items = 10))] n: Vec<String>) -> i32 {
todo!()
}
async fn min_items(&self, #[graphql(validator(min_items = 10))] n: Vec<String>) -> i32 {
todo!()
}
async fn chars_max_length(
&self,
#[graphql(validator(chars_max_length = 10))] n: String,
) -> i32 {
todo!()
}
async fn chars_length(
&self,
#[graphql(validator(chars_min_length = 10))] n: String,
) -> i32 {
todo!()
}
async fn email(&self, #[graphql(validator(email))] n: String) -> i32 {
todo!()
}
async fn list_email(&self, #[graphql(validator(list, email))] n: Vec<String>) -> i32 {
todo!()
}
}
}
#[tokio::test]
pub async fn test_validator_on_object_field_args() {
struct Query;