Rework validators 3
This commit is contained in:
parent
613bbc5b91
commit
71cbb9d20c
|
@ -1,17 +1,42 @@
|
||||||
use darling::util::SpannedValue;
|
use darling::util::SpannedValue;
|
||||||
use darling::FromMeta;
|
use darling::FromMeta;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::{quote, ToTokens};
|
||||||
use syn::{Expr, Result};
|
use syn::{Expr, Lit, Result};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Number {
|
||||||
|
F64(f64),
|
||||||
|
I64(i64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromMeta for Number {
|
||||||
|
fn from_value(value: &Lit) -> darling::Result<Self> {
|
||||||
|
match value {
|
||||||
|
Lit::Int(n) => Ok(Number::I64(n.base10_parse::<i64>()?)),
|
||||||
|
Lit::Float(n) => Ok(Number::F64(n.base10_parse::<f64>()?)),
|
||||||
|
_ => Err(darling::Error::unexpected_type("number")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for Number {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
match self {
|
||||||
|
Number::F64(n) => tokens.extend(quote!(#n as f64)),
|
||||||
|
Number::I64(n) => tokens.extend(quote!(#n as i64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromMeta, Default, Clone)]
|
#[derive(FromMeta, Default, Clone)]
|
||||||
pub struct Validators {
|
pub struct Validators {
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
multiple_of: Option<f64>,
|
multiple_of: Option<Number>,
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
maximum: Option<f64>,
|
maximum: Option<Number>,
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
minimum: Option<f64>,
|
minimum: Option<Number>,
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
max_length: Option<usize>,
|
max_length: Option<usize>,
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
|
@ -22,6 +47,8 @@ pub struct Validators {
|
||||||
min_items: Option<usize>,
|
min_items: Option<usize>,
|
||||||
#[darling(default, multiple)]
|
#[darling(default, multiple)]
|
||||||
custom: Vec<SpannedValue<String>>,
|
custom: Vec<SpannedValue<String>>,
|
||||||
|
#[darling(default)]
|
||||||
|
list: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Validators {
|
impl Validators {
|
||||||
|
@ -33,6 +60,13 @@ impl Validators {
|
||||||
map_err: Option<TokenStream>,
|
map_err: Option<TokenStream>,
|
||||||
) -> Result<TokenStream> {
|
) -> Result<TokenStream> {
|
||||||
let mut codes = Vec::new();
|
let mut codes = Vec::new();
|
||||||
|
let mut value = value;
|
||||||
|
let mut container = None;
|
||||||
|
|
||||||
|
if self.list {
|
||||||
|
container = Some(quote!(#value));
|
||||||
|
value = quote!(__item);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(n) = &self.multiple_of {
|
if let Some(n) = &self.multiple_of {
|
||||||
codes.push(quote! {
|
codes.push(quote! {
|
||||||
|
@ -85,6 +119,15 @@ impl Validators {
|
||||||
}
|
}
|
||||||
|
|
||||||
let codes = codes.into_iter().map(|s| quote!(#s #map_err ?));
|
let codes = codes.into_iter().map(|s| quote!(#s #map_err ?));
|
||||||
|
|
||||||
|
if let Some(container) = container {
|
||||||
|
Ok(quote! {
|
||||||
|
for __item in #container {
|
||||||
|
#(#codes;)*
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
Ok(quote!(#(#codes;)*))
|
Ok(quote!(#(#codes;)*))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub async fn test_validator_on_object_field_args() {
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl Query {
|
impl Query {
|
||||||
async fn value(&self, #[graphql(validator(maximum = "10"))] n: i32) -> i32 {
|
async fn value(&self, #[graphql(validator(maximum = 10))] n: i32) -> i32 {
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ pub async fn test_validator_on_object_field_args() {
|
||||||
pub async fn test_validator_on_input_object_field() {
|
pub async fn test_validator_on_input_object_field() {
|
||||||
#[derive(InputObject)]
|
#[derive(InputObject)]
|
||||||
struct MyInput {
|
struct MyInput {
|
||||||
#[graphql(validator(maximum = "10"))]
|
#[graphql(validator(maximum = 10))]
|
||||||
a: i32,
|
a: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ pub async fn test_validator_on_complex_object_field_args() {
|
||||||
|
|
||||||
#[ComplexObject]
|
#[ComplexObject]
|
||||||
impl Query {
|
impl Query {
|
||||||
async fn value(&self, #[graphql(validator(maximum = "10"))] n: i32) -> i32 {
|
async fn value(&self, #[graphql(validator(maximum = 10))] n: i32) -> i32 {
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ pub async fn test_validator_on_subscription_field_args() {
|
||||||
impl Subscription {
|
impl Subscription {
|
||||||
async fn value(
|
async fn value(
|
||||||
&self,
|
&self,
|
||||||
#[graphql(validator(maximum = "10"))] n: i32,
|
#[graphql(validator(maximum = 10))] n: i32,
|
||||||
) -> impl Stream<Item = i32> {
|
) -> impl Stream<Item = i32> {
|
||||||
futures_util::stream::iter(vec![n])
|
futures_util::stream::iter(vec![n])
|
||||||
}
|
}
|
||||||
|
@ -259,3 +259,45 @@ pub async fn test_custom_validator() {
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn test_list_validator() {
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
async fn value(&self, #[graphql(validator(maximum = 3, list))] n: Vec<i32>) -> i32 {
|
||||||
|
n.into_iter().sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
|
assert_eq!(
|
||||||
|
schema
|
||||||
|
.execute("{ value(n: [1, 2, 3]) }")
|
||||||
|
.await
|
||||||
|
.into_result()
|
||||||
|
.unwrap()
|
||||||
|
.data,
|
||||||
|
value!({ "value": 6 })
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
schema
|
||||||
|
.execute("{ value(n: [1, 2, 3, 4]) }")
|
||||||
|
.await
|
||||||
|
.into_result()
|
||||||
|
.unwrap_err(),
|
||||||
|
vec![ServerError {
|
||||||
|
message: r#"Failed to parse "Int": the value is 4, must be less than or equal to 3"#
|
||||||
|
.to_string(),
|
||||||
|
source: None,
|
||||||
|
locations: vec![Pos {
|
||||||
|
line: 1,
|
||||||
|
column: 12
|
||||||
|
}],
|
||||||
|
path: vec![PathSegment::Field("value".to_string())],
|
||||||
|
extensions: None
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user