v1.4.2
This commit is contained in:
parent
0d95cc6b0c
commit
934038150e
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "async-graphql"
|
name = "async-graphql"
|
||||||
version = "1.4.1"
|
version = "1.4.2"
|
||||||
authors = ["sunli <scott_s829@163.com>"]
|
authors = ["sunli <scott_s829@163.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "The GraphQL server library implemented by rust"
|
description = "The GraphQL server library implemented by rust"
|
||||||
|
@ -18,7 +18,7 @@ default = ["bson", "chrono", "uuid", "url", "validators"]
|
||||||
validators = ["regex", "once_cell"]
|
validators = ["regex", "once_cell"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-graphql-derive = { path = "async-graphql-derive", version = "1.4.1" }
|
async-graphql-derive = { path = "async-graphql-derive", version = "1.4.2" }
|
||||||
graphql-parser = "0.2.3"
|
graphql-parser = "0.2.3"
|
||||||
anyhow = "1.0.26"
|
anyhow = "1.0.26"
|
||||||
thiserror = "1.0.11"
|
thiserror = "1.0.11"
|
||||||
|
|
|
@ -109,10 +109,15 @@ Open `http://localhost:8000` in browser
|
||||||
- [X] IntRange
|
- [X] IntRange
|
||||||
- [X] IntLessThan
|
- [X] IntLessThan
|
||||||
- [X] IntGreaterThan
|
- [X] IntGreaterThan
|
||||||
|
- [X] IntNonZero
|
||||||
- [X] String
|
- [X] String
|
||||||
- [X] Email
|
- [X] Email
|
||||||
- [X] MAC
|
- [X] MAC
|
||||||
- [X] String
|
- [X] StringMinLength
|
||||||
|
- [X] StringMaxLength
|
||||||
|
- [X] List
|
||||||
|
- [X] ListMaxLength
|
||||||
|
- [X] ListMinLength
|
||||||
- [X] Subscription
|
- [X] Subscription
|
||||||
- [X] Filter
|
- [X] Filter
|
||||||
- [X] WebSocket transport
|
- [X] WebSocket transport
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "async-graphql-actix-web"
|
name = "async-graphql-actix-web"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
authors = ["sunli <scott_s829@163.com>"]
|
authors = ["sunli <scott_s829@163.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "async-graphql for actix-web"
|
description = "async-graphql for actix-web"
|
||||||
|
@ -13,7 +13,7 @@ keywords = ["futures", "async", "graphql"]
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-graphql = { path = "..", version = "1.4.1" }
|
async-graphql = { path = "..", version = "1.4.2" }
|
||||||
actix-web = "2.0.0"
|
actix-web = "2.0.0"
|
||||||
actix-multipart = "0.2.0"
|
actix-multipart = "0.2.0"
|
||||||
actix-web-actors = "2.0.0"
|
actix-web-actors = "2.0.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "async-graphql-derive"
|
name = "async-graphql-derive"
|
||||||
version = "1.4.1"
|
version = "1.4.2"
|
||||||
authors = ["sunli <scott_s829@163.com>"]
|
authors = ["sunli <scott_s829@163.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Macros for async-graphql"
|
description = "Macros for async-graphql"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::utils::{parse_validators, parse_value};
|
use crate::utils::{parse_validator, parse_value};
|
||||||
use graphql_parser::query::Value;
|
use graphql_parser::query::Value;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
@ -60,7 +60,7 @@ pub struct Argument {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
pub default: Option<Value>,
|
pub default: Option<Value>,
|
||||||
pub validators: TokenStream,
|
pub validator: TokenStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Argument {
|
impl Argument {
|
||||||
|
@ -68,11 +68,11 @@ impl Argument {
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
let mut desc = None;
|
let mut desc = None;
|
||||||
let mut default = None;
|
let mut default = None;
|
||||||
let mut validators = quote! { Default::default() };
|
let mut validator = quote! { None };
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
match attr.parse_meta() {
|
match attr.parse_meta()? {
|
||||||
Ok(Meta::List(ls)) if ls.path.is_ident("arg") => {
|
Meta::List(ls) if ls.path.is_ident("arg") => {
|
||||||
for meta in &ls.nested {
|
for meta in &ls.nested {
|
||||||
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
||||||
if nv.path.is_ident("name") {
|
if nv.path.is_ident("name") {
|
||||||
|
@ -120,7 +120,7 @@ impl Argument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validators = parse_validators(crate_name, &ls)?;
|
validator = parse_validator(crate_name, &ls)?;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ impl Argument {
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
default,
|
default,
|
||||||
validators,
|
validator,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,11 +150,11 @@ impl Field {
|
||||||
let mut deprecation = None;
|
let mut deprecation = None;
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
match attr.parse_meta() {
|
match attr.parse_meta()? {
|
||||||
Ok(Meta::Path(p)) if p.is_ident("field") => {
|
Meta::Path(p) if p.is_ident("field") => {
|
||||||
is_field = true;
|
is_field = true;
|
||||||
}
|
}
|
||||||
Ok(Meta::List(ls)) if ls.path.is_ident("field") => {
|
Meta::List(ls) if ls.path.is_ident("field") => {
|
||||||
is_field = true;
|
is_field = true;
|
||||||
for meta in &ls.nested {
|
for meta in &ls.nested {
|
||||||
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
||||||
|
@ -271,7 +271,7 @@ impl EnumItem {
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if attr.path.is_ident("item") {
|
if attr.path.is_ident("item") {
|
||||||
if let Ok(Meta::List(args)) = attr.parse_meta() {
|
if let Meta::List(args) = attr.parse_meta()? {
|
||||||
for meta in args.nested {
|
for meta in args.nested {
|
||||||
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
||||||
if nv.path.is_ident("name") {
|
if nv.path.is_ident("name") {
|
||||||
|
@ -322,7 +322,7 @@ pub struct InputField {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
pub default: Option<Value>,
|
pub default: Option<Value>,
|
||||||
pub validators: TokenStream,
|
pub validator: TokenStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputField {
|
impl InputField {
|
||||||
|
@ -331,11 +331,11 @@ impl InputField {
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
let mut desc = None;
|
let mut desc = None;
|
||||||
let mut default = None;
|
let mut default = None;
|
||||||
let mut validators = quote! { Default::default() };
|
let mut validator = quote! { None };
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if attr.path.is_ident("field") {
|
if attr.path.is_ident("field") {
|
||||||
if let Ok(Meta::List(args)) = &attr.parse_meta() {
|
if let Meta::List(args) = &attr.parse_meta()? {
|
||||||
for meta in &args.nested {
|
for meta in &args.nested {
|
||||||
match meta {
|
match meta {
|
||||||
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("internal") => {
|
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("internal") => {
|
||||||
|
@ -389,7 +389,7 @@ impl InputField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validators = parse_validators(crate_name, &args)?;
|
validator = parse_validator(crate_name, &args)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,7 +399,7 @@ impl InputField {
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
default,
|
default,
|
||||||
validators,
|
validator,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
|
||||||
let field_args = args::InputField::parse(&crate_name, &field.attrs)?;
|
let field_args = args::InputField::parse(&crate_name, &field.attrs)?;
|
||||||
let ident = field.ident.as_ref().unwrap();
|
let ident = field.ident.as_ref().unwrap();
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
let validators = &field_args.validators;
|
let validator = &field_args.validator;
|
||||||
let name = field_args
|
let name = field_args
|
||||||
.name
|
.name
|
||||||
.unwrap_or_else(|| ident.to_string().to_camel_case());
|
.unwrap_or_else(|| ident.to_string().to_camel_case());
|
||||||
|
@ -95,7 +95,7 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
|
||||||
description: #desc,
|
description: #desc,
|
||||||
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
||||||
default_value: #default,
|
default_value: #default,
|
||||||
validators: #validators,
|
validator: #validator,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
|
||||||
description: #desc,
|
description: #desc,
|
||||||
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
||||||
default_value: #schema_default,
|
default_value: #schema_default,
|
||||||
validators: Default::default(),
|
validator: None,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
default,
|
default,
|
||||||
validators,
|
validator,
|
||||||
},
|
},
|
||||||
) in args
|
) in args
|
||||||
{
|
{
|
||||||
|
@ -142,7 +142,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
description: #desc,
|
description: #desc,
|
||||||
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
||||||
default_value: #schema_default,
|
default_value: #schema_default,
|
||||||
validators: #validators,
|
validator: #validator,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
default,
|
default,
|
||||||
validators,
|
validator,
|
||||||
},
|
},
|
||||||
) in args
|
) in args
|
||||||
{
|
{
|
||||||
|
@ -166,7 +166,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
description: #desc,
|
description: #desc,
|
||||||
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
||||||
default_value: #schema_default,
|
default_value: #schema_default,
|
||||||
validators: #validators,
|
validator: #validator,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,10 @@ pub fn check_reserved_name(name: &str, internal: bool) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_validator(crate_name: &TokenStream, nested_meta: &NestedMeta) -> Result<TokenStream> {
|
pub fn parse_nested_validator(
|
||||||
|
crate_name: &TokenStream,
|
||||||
|
nested_meta: &NestedMeta,
|
||||||
|
) -> Result<TokenStream> {
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
match nested_meta {
|
match nested_meta {
|
||||||
|
@ -105,7 +108,7 @@ pub fn parse_validator(crate_name: &TokenStream, nested_meta: &NestedMeta) -> Re
|
||||||
if ls.path.is_ident("and") {
|
if ls.path.is_ident("and") {
|
||||||
let mut validators = Vec::new();
|
let mut validators = Vec::new();
|
||||||
for nested_meta in &ls.nested {
|
for nested_meta in &ls.nested {
|
||||||
validators.push(parse_validator(crate_name, nested_meta)?);
|
validators.push(parse_nested_validator(crate_name, nested_meta)?);
|
||||||
}
|
}
|
||||||
Ok(validators
|
Ok(validators
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -117,7 +120,7 @@ pub fn parse_validator(crate_name: &TokenStream, nested_meta: &NestedMeta) -> Re
|
||||||
} else if ls.path.is_ident("or") {
|
} else if ls.path.is_ident("or") {
|
||||||
let mut validators = Vec::new();
|
let mut validators = Vec::new();
|
||||||
for nested_meta in &ls.nested {
|
for nested_meta in &ls.nested {
|
||||||
validators.push(parse_validator(crate_name, nested_meta)?);
|
validators.push(parse_nested_validator(crate_name, nested_meta)?);
|
||||||
}
|
}
|
||||||
Ok(validators
|
Ok(validators
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -150,17 +153,24 @@ pub fn parse_validator(crate_name: &TokenStream, nested_meta: &NestedMeta) -> Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_validators(crate_name: &TokenStream, args: &MetaList) -> Result<TokenStream> {
|
pub fn parse_validator(crate_name: &TokenStream, args: &MetaList) -> Result<TokenStream> {
|
||||||
let mut validators = Vec::new();
|
|
||||||
for arg in &args.nested {
|
for arg in &args.nested {
|
||||||
if let NestedMeta::Meta(Meta::List(ls)) = arg {
|
if let NestedMeta::Meta(Meta::List(ls)) = arg {
|
||||||
if ls.path.is_ident("validators") {
|
if ls.path.is_ident("validator") {
|
||||||
for meta in &ls.nested {
|
if ls.nested.len() > 1 {
|
||||||
let validator = parse_validator(crate_name, meta)?;
|
return Err(Error::new_spanned(ls,
|
||||||
validators.push(quote! { Box::new(#validator) });
|
"Only one validator can be defined. You can connect combine validators with `and` or `or`"));
|
||||||
}
|
}
|
||||||
|
if ls.nested.len() == 0 {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
ls,
|
||||||
|
"At least one validator must be defined",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let validator = parse_nested_validator(crate_name, &ls.nested[0])?;
|
||||||
|
return Ok(quote! { Some(std::sync::Arc::new(#validator)) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(quote! { std::sync::Arc::new(vec![#(#validators),*]) })
|
Ok(quote! {None})
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ pub use types::{EnumItem, EnumType};
|
||||||
/// | name | Argument name | string | Y |
|
/// | name | Argument name | string | Y |
|
||||||
/// | desc | Argument description | string | Y |
|
/// | desc | Argument description | string | Y |
|
||||||
/// | default | Argument default value | string | Y |
|
/// | default | Argument default value | string | Y |
|
||||||
/// | validators | Input value validators | `InputValueValidator` | Y |
|
/// | validator | Input value validator | [`InputValueValidator`](validators/trait.InputValueValidator.html) | Y |
|
||||||
///
|
///
|
||||||
/// # The field returns the value type
|
/// # The field returns the value type
|
||||||
///
|
///
|
||||||
|
@ -300,7 +300,7 @@ pub use async_graphql_derive::Enum;
|
||||||
/// | name | Field name | string | Y |
|
/// | name | Field name | string | Y |
|
||||||
/// | desc | Field description | string | Y |
|
/// | desc | Field description | string | Y |
|
||||||
/// | default | Field default value | string | Y |
|
/// | default | Field default value | string | Y |
|
||||||
/// | validators | Input value validators | `InputValueValidator` | Y |
|
/// | validator | Input value validator | [`InputValueValidator`](validators/trait.InputValueValidator.html) | Y |
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -355,7 +355,7 @@ pub use async_graphql_derive::InputObject;
|
||||||
/// | desc | Field description | string | Y |
|
/// | desc | Field description | string | Y |
|
||||||
/// | context | Method with the context | string | Y |
|
/// | context | Method with the context | string | Y |
|
||||||
/// | deprecation | Field deprecation reason | string | Y |
|
/// | deprecation | Field deprecation reason | string | Y |
|
||||||
/// | args | Field arguments | [Arg] | Y |
|
/// | args | Field arguments | | Y |
|
||||||
///
|
///
|
||||||
/// # Field argument parameters
|
/// # Field argument parameters
|
||||||
///
|
///
|
||||||
|
@ -485,7 +485,7 @@ pub use async_graphql_derive::Union;
|
||||||
/// | name | Argument name | string | Y |
|
/// | name | Argument name | string | Y |
|
||||||
/// | desc | Argument description | string | Y |
|
/// | desc | Argument description | string | Y |
|
||||||
/// | default | Argument default value | string | Y |
|
/// | default | Argument default value | string | Y |
|
||||||
/// | validators | Input value validators | `InputValueValidator` | Y |
|
/// | validator | Input value validator | [`InputValueValidator`](validators/trait.InputValueValidator.html) | Y |
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
|
|
@ -60,7 +60,7 @@ pub struct InputValue {
|
||||||
pub description: Option<&'static str>,
|
pub description: Option<&'static str>,
|
||||||
pub ty: String,
|
pub ty: String,
|
||||||
pub default_value: Option<&'static str>,
|
pub default_value: Option<&'static str>,
|
||||||
pub validators: Arc<Vec<Box<dyn InputValueValidator>>>,
|
pub validator: Option<Arc<dyn InputValueValidator>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl<Query: ObjectType, Mutation: ObjectType, Subscription: SubscriptionType>
|
||||||
description: Some("Included when true."),
|
description: Some("Included when true."),
|
||||||
ty: "Boolean!".to_string(),
|
ty: "Boolean!".to_string(),
|
||||||
default_value: None,
|
default_value: None,
|
||||||
validators: Default::default(),
|
validator: None,
|
||||||
});
|
});
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ impl<Query: ObjectType, Mutation: ObjectType, Subscription: SubscriptionType>
|
||||||
description: Some("Skipped when true."),
|
description: Some("Skipped when true."),
|
||||||
ty: "Boolean!".to_string(),
|
ty: "Boolean!".to_string(),
|
||||||
default_value: None,
|
default_value: None,
|
||||||
validators: Default::default(),
|
validator: None,
|
||||||
});
|
});
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl<T: Type> Type for QueryRoot<T> {
|
||||||
description: None,
|
description: None,
|
||||||
ty: "String!".to_string(),
|
ty: "String!".to_string(),
|
||||||
default_value: None,
|
default_value: None,
|
||||||
validators: Default::default(),
|
validator: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
args
|
args
|
||||||
|
|
|
@ -36,7 +36,7 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
|
||||||
.current_args
|
.current_args
|
||||||
.and_then(|args| args.get(name).map(|input| input))
|
.and_then(|args| args.get(name).map(|input| input))
|
||||||
{
|
{
|
||||||
for validator in arg.validators.iter() {
|
if let Some(validator) = &arg.validator {
|
||||||
if let Some(reason) = validator.is_valid(value) {
|
if let Some(reason) = validator.is_valid(value) {
|
||||||
ctx.report_error(
|
ctx.report_error(
|
||||||
vec![pos],
|
vec![pos],
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl InputValueValidator for IntRange {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some("expected type \"Int\"".to_string())
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ impl InputValueValidator for IntLessThan {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some("expected type \"Int\"".to_string())
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,3 +76,23 @@ impl InputValueValidator for IntGreaterThan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Integer nonzero validator
|
||||||
|
pub struct IntNonZero {}
|
||||||
|
|
||||||
|
impl InputValueValidator for IntNonZero {
|
||||||
|
fn is_valid(&self, value: &Value) -> Option<String> {
|
||||||
|
if let Value::Int(n) = value {
|
||||||
|
if n.as_i64().unwrap() == 0 {
|
||||||
|
Some(format!(
|
||||||
|
"the value is {}, but must be nonzero",
|
||||||
|
n.as_i64().unwrap(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some("expected type \"Int\"".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
50
src/validators/list_validators.rs
Normal file
50
src/validators/list_validators.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use crate::validators::InputValueValidator;
|
||||||
|
use graphql_parser::query::Value;
|
||||||
|
|
||||||
|
/// List minimum length validator
|
||||||
|
pub struct ListMinLength {
|
||||||
|
/// Must be greater than or equal to this value.
|
||||||
|
pub length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputValueValidator for ListMinLength {
|
||||||
|
fn is_valid(&self, value: &Value) -> Option<String> {
|
||||||
|
if let Value::List(values) = value {
|
||||||
|
if values.len() < self.length {
|
||||||
|
Some(format!(
|
||||||
|
"The value length is {}, but the length must be greater than or equal to {}",
|
||||||
|
values.len(),
|
||||||
|
self.length
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some("expected type \"List\"".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List maximum length validator
|
||||||
|
pub struct ListMaxLength {
|
||||||
|
/// Must be less than or equal to this value.
|
||||||
|
pub length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputValueValidator for ListMaxLength {
|
||||||
|
fn is_valid(&self, value: &Value) -> Option<String> {
|
||||||
|
if let Value::List(values) = value {
|
||||||
|
if values.len() > self.length {
|
||||||
|
Some(format!(
|
||||||
|
"The value length is {}, but the length must be less than or equal to {}",
|
||||||
|
values.len(),
|
||||||
|
self.length
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
mod int_validators;
|
mod int_validators;
|
||||||
|
mod list_validators;
|
||||||
mod string_validators;
|
mod string_validators;
|
||||||
|
|
||||||
use graphql_parser::schema::Value;
|
use graphql_parser::schema::Value;
|
||||||
|
|
||||||
pub use int_validators::{IntGreaterThan, IntLessThan, IntRange};
|
pub use int_validators::{IntGreaterThan, IntLessThan, IntNonZero, IntRange};
|
||||||
pub use string_validators::{Email, MAC};
|
pub use list_validators::{ListMaxLength, ListMinLength};
|
||||||
|
pub use string_validators::{Email, StringMaxLength, StringMinLength, MAC};
|
||||||
|
|
||||||
/// Input value validator
|
/// Input value validator
|
||||||
///
|
///
|
||||||
|
@ -20,19 +22,19 @@ pub use string_validators::{Email, MAC};
|
||||||
/// impl QueryRoot {
|
/// impl QueryRoot {
|
||||||
/// // Input is email address
|
/// // Input is email address
|
||||||
/// #[field]
|
/// #[field]
|
||||||
/// async fn value1(&self, #[arg(validators(Email))] email: String) -> i32 {
|
/// async fn value1(&self, #[arg(validator(Email))] email: String) -> i32 {
|
||||||
/// unimplemented!()
|
/// unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// // Input is email or MAC address
|
/// // Input is email or MAC address
|
||||||
/// #[field]
|
/// #[field]
|
||||||
/// async fn value2(&self, #[arg(validators(or(Email, MAC(colon: false))))] email_or_mac: String) -> i32 {
|
/// async fn value2(&self, #[arg(validator(or(Email, MAC(colon = false))))] email_or_mac: String) -> i32 {
|
||||||
/// unimplemented!()
|
/// unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// // Input is integer between 100 and 200
|
/// // Input is integer between 100 and 200
|
||||||
/// #[field]
|
/// #[field]
|
||||||
/// async fn value3(&self, #[arg(validators(IntRange(min = 100, max = 200)))] value: i32) -> i32 {
|
/// async fn value3(&self, #[arg(validator(IntRange(min = 100, max = 200)))] value: i32) -> i32 {
|
||||||
/// unimplemented!()
|
/// unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
@ -42,6 +44,8 @@ where
|
||||||
Self: Sync + Send,
|
Self: Sync + Send,
|
||||||
{
|
{
|
||||||
/// Check value is valid, returns the reason for the error if it fails, otherwise None.
|
/// Check value is valid, returns the reason for the error if it fails, otherwise None.
|
||||||
|
///
|
||||||
|
/// If the input type is different from the required type, return None directly, and other validators will find this error.
|
||||||
fn is_valid(&self, value: &Value) -> Option<String>;
|
fn is_valid(&self, value: &Value) -> Option<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,54 @@ use graphql_parser::schema::Value;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
|
/// String minimum length validator
|
||||||
|
pub struct StringMinLength {
|
||||||
|
/// Must be greater than or equal to this value.
|
||||||
|
pub length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputValueValidator for StringMinLength {
|
||||||
|
fn is_valid(&self, value: &Value) -> Option<String> {
|
||||||
|
if let Value::String(s) = value {
|
||||||
|
if s.len() < self.length {
|
||||||
|
Some(format!(
|
||||||
|
"The value length is {}, but the length must be greater than or equal to {}",
|
||||||
|
s.len(),
|
||||||
|
self.length
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// String maximum length validator
|
||||||
|
pub struct StringMaxLength {
|
||||||
|
/// Must be less than or equal to this value.
|
||||||
|
pub length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputValueValidator for StringMaxLength {
|
||||||
|
fn is_valid(&self, value: &Value) -> Option<String> {
|
||||||
|
if let Value::String(s) = value {
|
||||||
|
if s.len() > self.length {
|
||||||
|
Some(format!(
|
||||||
|
"The value length is {}, but the length must be less than or equal to {}",
|
||||||
|
s.len(),
|
||||||
|
self.length
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static EMAIL_RE: Lazy<Regex> = Lazy::new(|| {
|
static EMAIL_RE: Lazy<Regex> = Lazy::new(|| {
|
||||||
Regex::new("^(([0-9A-Za-z!#$%&'*+-/=?^_`{|}~&&[^@]]+)|(\"([0-9A-Za-z!#$%&'*+-/=?^_`{|}~ \"(),:;<>@\\[\\\\\\]]+)\"))@").unwrap()
|
Regex::new("^(([0-9A-Za-z!#$%&'*+-/=?^_`{|}~&&[^@]]+)|(\"([0-9A-Za-z!#$%&'*+-/=?^_`{|}~ \"(),:;<>@\\[\\\\\\]]+)\"))@").unwrap()
|
||||||
});
|
});
|
||||||
|
@ -19,7 +67,7 @@ impl InputValueValidator for Email {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some("expected type \"String\"".to_string())
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,8 +79,8 @@ static MAC_ADDRESS_NO_COLON_RE: Lazy<Regex> =
|
||||||
|
|
||||||
/// MAC address validator
|
/// MAC address validator
|
||||||
pub struct MAC {
|
pub struct MAC {
|
||||||
/// Must include colon
|
/// Must include colon.
|
||||||
colon: bool,
|
pub colon: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputValueValidator for MAC {
|
impl InputValueValidator for MAC {
|
||||||
|
@ -50,7 +98,7 @@ impl InputValueValidator for MAC {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some("expected type \"String\"".to_string())
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user