Add some validators
This commit is contained in:
parent
4b1cb058fa
commit
5a1f0ed865
|
@ -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()))?;
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue