Implement a type-safe default value definition for InputValue. #111

This commit is contained in:
Sunli 2020-05-26 18:34:43 +08:00 committed by sunli
parent 69367dc65f
commit a9d3ac9cf8
42 changed files with 244 additions and 300 deletions

View File

@ -1,5 +1,4 @@
use crate::utils::{get_rustdoc, parse_guards, parse_validator};
use async_graphql_parser::{parse_value, ParsedValue};
use crate::utils::{get_rustdoc, parse_default, parse_default_with, parse_guards, parse_validator};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Attribute, AttributeArgs, Error, Lit, Meta, MetaList, NestedMeta, Result, Type};
@ -126,7 +125,7 @@ impl Object {
pub struct Argument {
pub name: Option<String>,
pub desc: Option<String>,
pub default: Option<ParsedValue>,
pub default: Option<TokenStream>,
pub validator: TokenStream,
}
@ -141,7 +140,11 @@ impl Argument {
match attr.parse_meta()? {
Meta::List(ls) if ls.path.is_ident("arg") => {
for meta in &ls.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
if let NestedMeta::Meta(Meta::Path(p)) = meta {
if p.is_ident("default") {
default = Some(quote! { Default::default() });
}
} else if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
if nv.path.is_ident("name") {
if let syn::Lit::Str(lit) = &nv.lit {
name = Some(lit.value());
@ -161,22 +164,9 @@ impl Argument {
));
}
} else if nv.path.is_ident("default") {
if let syn::Lit::Str(lit) = &nv.lit {
match parse_value(&lit.value()) {
Ok(value) => default = Some(value),
Err(err) => {
return Err(Error::new_spanned(
&nv.lit,
format!("Invalid value: {}", err),
));
}
}
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'default' should be a string.",
));
}
default = Some(parse_default(&nv.lit)?);
} else if nv.path.is_ident("default_with") {
default = Some(parse_default_with(&nv.lit)?);
}
}
}
@ -430,7 +420,7 @@ impl EnumItem {
pub struct InputField {
pub name: Option<String>,
pub desc: Option<String>,
pub default: Option<ParsedValue>,
pub default: Option<TokenStream>,
pub validator: TokenStream,
}
@ -452,6 +442,9 @@ impl InputField {
"Fields on InputObject are not allowed to be skipped",
));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("default") => {
default = Some(quote! { Default::default() });
}
NestedMeta::Meta(Meta::NameValue(nv)) => {
if nv.path.is_ident("name") {
if let syn::Lit::Str(lit) = &nv.lit {
@ -472,22 +465,9 @@ impl InputField {
));
}
} else if nv.path.is_ident("default") {
if let syn::Lit::Str(lit) = &nv.lit {
match parse_value(&lit.value()) {
Ok(value) => default = Some(value),
Err(err) => {
return Err(Error::new_spanned(
&lit,
format!("Invalid value: {}", err),
));
}
}
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'default' should be a string.",
));
}
default = Some(parse_default(&nv.lit)?);
} else if nv.path.is_ident("default_with") {
default = Some(parse_default_with(&nv.lit)?);
}
}
_ => {}
@ -566,7 +546,7 @@ pub struct InterfaceFieldArgument {
pub name: String,
pub desc: Option<String>,
pub ty: Type,
pub default: Option<ParsedValue>,
pub default: Option<TokenStream>,
}
impl InterfaceFieldArgument {
@ -577,7 +557,11 @@ impl InterfaceFieldArgument {
let mut default = None;
for meta in &ls.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
if let NestedMeta::Meta(Meta::Path(p)) = meta {
if p.is_ident("default") {
default = Some(quote! { Default::default() });
}
} else if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
if nv.path.is_ident("name") {
if let syn::Lit::Str(lit) = &nv.lit {
name = Some(lit.value());
@ -610,22 +594,9 @@ impl InterfaceFieldArgument {
));
}
} else if nv.path.is_ident("default") {
if let syn::Lit::Str(lit) = &nv.lit {
match parse_value(&lit.value()) {
Ok(value) => default = Some(value),
Err(err) => {
return Err(Error::new_spanned(
&nv.lit,
format!("Invalid value: {}", err),
));
}
}
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'default' should be a string.",
));
}
default = Some(parse_default(&nv.lit)?);
} else if nv.path.is_ident("default_with") {
default = Some(parse_default_with(&nv.lit)?);
}
}
}

View File

@ -159,12 +159,16 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
fn parse(value: #crate_name::Value) -> #crate_name::InputValueResult<Self> {
#crate_name::EnumType::parse_enum(value)
}
fn to_value(&self) -> #crate_name::Value {
#crate_name::EnumType::to_value(self)
}
}
#[#crate_name::async_trait::async_trait]
impl #crate_name::OutputValueType for #ident {
async fn resolve(&self, _: &#crate_name::ContextSelectionSet<'_>, _field: &#crate_name::Positioned<#crate_name::parser::query::Field>) -> #crate_name::Result<#crate_name::serde_json::Value> {
#crate_name::EnumType::resolve_enum(self)
Ok(#crate_name::EnumType::to_value(self).into())
}
}
};

View File

@ -1,5 +1,5 @@
use crate::args;
use crate::utils::{build_value_repr, check_reserved_name, get_crate_name, get_rustdoc};
use crate::utils::{check_reserved_name, get_crate_name, get_rustdoc};
use inflector::Inflector;
use proc_macro::TokenStream;
use quote::quote;
@ -51,6 +51,7 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
.unwrap_or_else(|| quote! {None});
let mut get_fields = Vec::new();
let mut put_fields = Vec::new();
let mut fields = Vec::new();
let mut schema_fields = Vec::new();
@ -67,25 +68,20 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
.as_ref()
.map(|s| quote! {Some(#s)})
.unwrap_or_else(|| quote! {None});
let default = field_args
let schema_default = field_args
.default
.as_ref()
.map(|v| {
let s = v.to_string();
quote! {Some(#s)}
.map(|value| {
quote! {Some( #crate_name::InputValueType::to_value(&#value).to_string() )}
})
.unwrap_or_else(|| quote! {None});
if let Some(default) = &field_args.default {
let default_repr = build_value_repr(&crate_name, default);
get_fields.push(quote! {
let #ident:#ty = {
let #ident: #ty = {
match obj.get(#name) {
Some(value) => #crate_name::InputValueType::parse(value.clone())?,
None => {
let default = #default_repr;
#crate_name::InputValueType::parse(default)?
}
None => #default,
}
};
});
@ -95,13 +91,17 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
});
}
put_fields.push(quote! {
map.insert(#name.to_string(), #crate_name::InputValueType::to_value(&self.#ident));
});
fields.push(ident);
schema_fields.push(quote! {
fields.insert(#name.to_string(), #crate_name::registry::MetaInputValue {
name: #name,
description: #desc,
ty: <#ty as #crate_name::Type>::create_type_info(registry),
default_value: #default,
default_value: #schema_default,
validator: #validator,
});
})
@ -139,9 +139,18 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
Err(#crate_name::InputValueError::ExpectedType(value))
}
}
fn to_value(&self) -> #crate_name::Value {
let mut map = std::collections::BTreeMap::new();
#(#put_fields)*
#crate_name::Value::Object(map)
}
}
impl #crate_name::InputObjectType for #ident {}
};
if gql_typename == "MyInput222" {
println!("{}", expanded);
}
Ok(expanded.into())
}

View File

@ -1,7 +1,7 @@
use crate::args;
use crate::args::{InterfaceField, InterfaceFieldArgument};
use crate::output_type::OutputType;
use crate::utils::{build_value_repr, check_reserved_name, get_crate_name, get_rustdoc};
use crate::utils::{check_reserved_name, get_crate_name, get_rustdoc};
use inflector::Inflector;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
@ -154,15 +154,12 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
decl_params.push(quote! { #ident: #ty });
use_params.push(quote! { #ident });
let param_default = match &default {
Some(default) => {
let repr = build_value_repr(&crate_name, &default);
quote! {|| #repr }
}
None => quote! { || #crate_name::Value::Null },
let get_default = match default {
Some(default) => quote! { Some(|| -> #ty { #default }) },
None => quote! { None },
};
get_params.push(quote! {
let #ident: #ty = ctx.param_value(#name, #param_default)?;
let #ident: #ty = ctx.param_value(#name, #get_default)?;
});
let desc = desc
@ -171,9 +168,8 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
.unwrap_or_else(|| quote! {None});
let schema_default = default
.as_ref()
.map(|v| {
let s = v.to_string();
quote! {Some(#s)}
.map(|value| {
quote! {Some( <#ty as #crate_name::InputValueType>::to_value(&#value).to_string() )}
})
.unwrap_or_else(|| quote! {None});
schema_args.push(quote! {

View File

@ -1,6 +1,6 @@
use crate::args;
use crate::output_type::OutputType;
use crate::utils::{build_value_repr, check_reserved_name, get_crate_name, get_rustdoc};
use crate::utils::{check_reserved_name, get_crate_name, get_rustdoc};
use inflector::Inflector;
use proc_macro::TokenStream;
use quote::quote;
@ -303,9 +303,8 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
.unwrap_or_else(|| quote! {None});
let schema_default = default
.as_ref()
.map(|v| {
let s = v.to_string();
quote! {Some(#s)}
.map(|value| {
quote! {Some( <#ty as #crate_name::InputValueType>::to_value(&#value).to_string() )}
})
.unwrap_or_else(|| quote! {None});
@ -321,16 +320,10 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
use_params.push(quote! { #ident });
let default = match &default {
Some(default) => {
let repr = build_value_repr(&crate_name, &default);
quote! {|| #repr }
}
None => {
quote! { || #crate_name::Value::Null }
}
let default = match default {
Some(default) => quote! { Some(|| -> #ty { #default }) },
None => quote! { None },
};
get_params.push(quote! {
let #ident: #ty = ctx.param_value(#name, #default)?;
});

View File

@ -50,6 +50,10 @@ pub fn generate(scalar_args: &args::Scalar, item_impl: &mut ItemImpl) -> Result<
fn parse(value: #crate_name::Value) -> #crate_name::InputValueResult<Self> {
<#self_ty as #crate_name::ScalarType>::parse(value)
}
fn to_value(&self) -> Value {
<#self_ty as #crate_name::ScalarType>::to_value(self)
}
}
#[allow(clippy::ptr_arg)]
@ -60,7 +64,7 @@ pub fn generate(scalar_args: &args::Scalar, item_impl: &mut ItemImpl) -> Result<
_: &#crate_name::ContextSelectionSet<'_>,
_field: &#crate_name::Positioned<#crate_name::parser::query::Field>
) -> #crate_name::Result<#crate_name::serde_json::Value> {
self.to_json()
Ok(#crate_name::ScalarType::to_value(self).into())
}
}
};

View File

@ -1,6 +1,6 @@
use crate::args;
use crate::output_type::OutputType;
use crate::utils::{build_value_repr, check_reserved_name, get_crate_name, get_rustdoc};
use crate::utils::{check_reserved_name, get_crate_name, get_rustdoc};
use inflector::Inflector;
use proc_macro::TokenStream;
use quote::quote;
@ -155,9 +155,8 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
.unwrap_or_else(|| quote! {None});
let schema_default = default
.as_ref()
.map(|v| {
let s = v.to_string();
quote! {Some(#s)}
.map(|value| {
quote! {Some( <#ty as #crate_name::InputValueType>::to_value(&#value).to_string() )}
})
.unwrap_or_else(|| quote! {None});
@ -173,14 +172,10 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
use_params.push(quote! { #ident });
let default = match &default {
Some(default) => {
let repr = build_value_repr(&crate_name, &default);
quote! {|| #repr }
}
None => quote! { || #crate_name::Value::Null },
let default = match default {
Some(default) => quote! { Some(|| -> #ty { #default }) },
None => quote! { None },
};
get_params.push(quote! {
let #ident: #ty = ctx.param_value(#name, #default)?;
});

View File

@ -1,4 +1,3 @@
use async_graphql_parser::Value;
use proc_macro2::{Span, TokenStream};
use proc_macro_crate::crate_name;
use quote::quote;
@ -14,56 +13,6 @@ pub fn get_crate_name(internal: bool) -> TokenStream {
}
}
pub fn build_value_repr(crate_name: &TokenStream, value: &Value) -> TokenStream {
match value {
Value::Variable(_) => unreachable!(),
Value::Int(n) => {
quote! { #crate_name::Value::Int(#n) }
}
Value::Float(n) => {
quote! { #crate_name::Value::Float(#n) }
}
Value::String(s) => {
quote! { #crate_name::Value::String(#s.to_string().into()) }
}
Value::Boolean(n) => {
quote! { #crate_name::Value::Boolean(#n) }
}
Value::Null => {
quote! { #crate_name::Value::Null }
}
Value::Enum(n) => {
quote! { #crate_name::Value::Enum(#n.to_string()) }
}
Value::List(ls) => {
let members = ls
.iter()
.map(|v| build_value_repr(crate_name, v))
.collect::<Vec<_>>();
quote! { #crate_name::Value::List(vec![#(#members),*]) }
}
Value::Object(obj) => {
let members = obj
.iter()
.map(|(n, v)| {
let value = build_value_repr(crate_name, v);
quote! {
obj.insert(#n.to_string().into(), #value);
}
})
.collect::<Vec<_>>();
quote! {
{
let mut obj = std::collections::BTreeMap::new();
#(#members)*
#crate_name::Value::Object(obj)
}
}
}
Value::Upload(_) => quote! { #crate_name::Value::Null },
}
}
pub fn check_reserved_name(name: &str, internal: bool) -> Result<()> {
if internal {
return Ok(());
@ -244,3 +193,41 @@ pub fn get_rustdoc(attrs: &[Attribute]) -> Result<Option<String>> {
Some(full_docs)
})
}
pub fn parse_default(lit: &Lit) -> Result<TokenStream> {
match lit {
Lit::Str(value) =>{
let value = value.value();
Ok(quote!({ #value.to_string() }))
}
Lit::Int(value) => {
let value = value.base10_parse::<i32>()?;
Ok(quote!({ #value as i32 }))
}
Lit::Float(value) => {
let value = value.base10_parse::<f64>()?;
Ok(quote!({ #value as f64 }))
}
Lit::Bool(value) => {
let value = value.value;
Ok(quote!({ #value }))
}
_ => Err(Error::new_spanned(
lit,
"The default value type only be string, integer, float and boolean, other types should use default_with",
)),
}
}
pub fn parse_default_with(lit: &Lit) -> Result<TokenStream> {
if let Lit::Str(str) = lit {
let str = str.value();
let tokens: TokenStream = str.parse()?;
Ok(quote! { #tokens })
} else {
Err(Error::new_spanned(
&lit,
"Attribute 'default' should be a string.",
))
}
}

View File

@ -15,6 +15,6 @@ mod value;
pub use error::{Error, Result};
pub use pos::{Pos, Positioned};
pub use query_parser::{parse_query, parse_value, ParsedValue};
pub use query_parser::parse_query;
pub use schema_parser::parse_schema;
pub use value::{UploadValue, Value};

View File

@ -6,8 +6,6 @@ use crate::Result;
use pest::iterators::Pair;
use pest::Parser;
use std::collections::BTreeMap;
use std::fmt;
use std::ops::Deref;
#[derive(Parser)]
#[grammar = "query.pest"]
@ -44,35 +42,6 @@ pub fn parse_query<T: AsRef<str>>(input: T) -> Result<Document> {
})
}
pub struct ParsedValue {
#[allow(dead_code)]
source: String,
value: Value,
}
impl fmt::Debug for ParsedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl Deref for ParsedValue {
type Target = Value;
fn deref(&self) -> &Self::Target {
&self.value
}
}
/// Parse a graphql value
pub fn parse_value<T: Into<String>>(input: T) -> Result<ParsedValue> {
let source = input.into();
let value_pair: Pair<Rule> = QueryParser::parse(Rule::value, &source)?.next().unwrap();
let mut pc = PositionCalculator::new(&source);
let value = parse_value2(value_pair, &mut pc)?;
Ok(ParsedValue { source, value })
}
fn parse_named_operation_definition(
pair: Pair<Rule>,
pc: &mut PositionCalculator,

View File

@ -31,7 +31,7 @@ impl Clone for UploadValue {
pub enum Value {
Null,
Variable(String),
Int(i64),
Int(i32),
Float(f64),
String(String),
Boolean(bool),
@ -174,7 +174,7 @@ impl From<serde_json::Value> for Value {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(n) => Value::Boolean(n),
serde_json::Value::Number(n) if n.is_f64() => Value::Float(n.as_f64().unwrap()),
serde_json::Value::Number(n) => Value::Int(n.as_i64().unwrap()),
serde_json::Value::Number(n) => Value::Int(n.as_i64().unwrap() as i32),
serde_json::Value::String(s) => Value::String(s),
serde_json::Value::Array(ls) => Value::List(ls.into_iter().map(Into::into).collect()),
serde_json::Value::Object(obj) => Value::Object(

View File

@ -53,6 +53,9 @@ pub trait Type {
pub trait InputValueType: Type + Sized {
/// Parse from `Value`
fn parse(value: Value) -> InputValueResult<Self>;
/// Convert to `Value` for introspection
fn to_value(&self) -> Value;
}
/// Represents a GraphQL output value
@ -137,8 +140,8 @@ pub trait InputObjectType: InputValueType {}
/// }
/// }
///
/// fn to_json(&self) -> Result<serde_json::Value> {
/// Ok(self.0.into())
/// fn to_value(&self) -> Value {
/// Value::Int(self.0)
/// }
/// }
/// ```
@ -153,8 +156,8 @@ pub trait ScalarType: Sized + Send {
true
}
/// Convert the scalar value to json value.
fn to_json(&self) -> Result<serde_json::Value>;
/// Convert the scalar to `Value`.
fn to_value(&self) -> Value;
}
impl<T: Type + Send + Sync> Type for &T {

View File

@ -532,13 +532,13 @@ impl<'a> ContextBase<'a, &'a Positioned<SelectionSet>> {
impl<'a> ContextBase<'a, &'a Positioned<Field>> {
#[doc(hidden)]
pub fn param_value<T: InputValueType, F: FnOnce() -> Value>(
pub fn param_value<T: InputValueType>(
&self,
name: &str,
default: F,
default: Option<fn() -> T>,
) -> Result<T> {
match self.get_argument(name).cloned() {
Some(value) => {
match (self.get_argument(name).cloned(), default) {
(Some(value), _) => {
let pos = value.position();
let value = self.resolve_input_value(value.into_inner(), pos)?;
match InputValueType::parse(value) {
@ -546,16 +546,9 @@ impl<'a> ContextBase<'a, &'a Positioned<Field>> {
Err(err) => Err(err.into_error(pos, T::qualified_type_name())),
}
}
None => {
let value = default();
match InputValueType::parse(value) {
Ok(res) => Ok(res),
Err(err) => {
// The default value has no valid location.
Err(err.into_error(Pos::default(), T::qualified_type_name()))
}
}
}
(None, Some(default)) => Ok(default()),
(None, None) => InputValueType::parse(Value::Null)
.map_err(|err| err.into_error(Pos::default(), T::qualified_type_name())),
}
}

View File

@ -257,7 +257,7 @@ pub use types::{EnumItem, EnumType};
/// Ok(self.value)
/// }
///
/// async fn value_with_arg(&self, #[arg(default = "1")] a: i32) -> i32 {
/// async fn value_with_arg(&self, #[arg(default = 1)] a: i32) -> i32 {
/// a
/// }
/// }
@ -411,7 +411,7 @@ pub use async_graphql_derive::Enum;
/// #[InputObject]
/// struct MyInputObject {
/// a: i32,
/// #[field(default = "10")]
/// #[field(default = 10)]
/// b: i32,
/// }
///

View File

@ -24,6 +24,6 @@ impl<'a> __InputValue<'a> {
}
async fn default_value(&self) -> Option<String> {
self.input_value.default_value.map(|s| s.to_string())
self.input_value.default_value.clone()
}
}

View File

@ -41,8 +41,8 @@ impl<'a> __Type<'a> {
}
/// The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.
//
// Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.
///
/// Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.
#[Object(internal)]
impl<'a> __Type<'a> {
async fn kind(&self) -> __TypeKind {
@ -93,7 +93,7 @@ impl<'a> __Type<'a> {
async fn fields(
&self,
#[arg(default = "false")] include_deprecated: bool,
#[arg(default = false)] include_deprecated: bool,
) -> Option<Vec<__Field<'a>>> {
if let TypeDetail::Named(ty) = &self.detail {
ty.fields().map(|fields| {
@ -156,7 +156,7 @@ impl<'a> __Type<'a> {
async fn enum_values(
&self,
#[arg(default = "false")] include_deprecated: bool,
#[arg(default = false)] include_deprecated: bool,
) -> Option<Vec<__EnumValue<'a>>> {
if let TypeDetail::Named(registry::MetaType::Enum { enum_values, .. }) = &self.detail {
Some(

View File

@ -97,7 +97,7 @@ pub struct MetaInputValue {
pub name: &'static str,
pub description: Option<&'static str>,
pub ty: String,
pub default_value: Option<&'static str>,
pub default_value: Option<String>,
pub validator: Option<Arc<dyn InputValueValidator>>,
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueResult, Result, ScalarType, Value};
use crate::{InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
use serde::de::DeserializeOwned;
@ -19,15 +19,15 @@ impl ScalarType for Any {
true
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.0.clone().into())
fn to_value(&self) -> Value {
self.0.clone()
}
}
impl Any {
/// Parse this `Any` value to T by `serde_json`.
pub fn parse_value<T: DeserializeOwned>(&self) -> std::result::Result<T, serde_json::Error> {
serde_json::from_value(self.to_json().unwrap())
serde_json::from_value(self.to_value().into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
/// The `Boolean` scalar type represents `true` or `false`.
@ -18,7 +18,7 @@ impl ScalarType for bool {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into())
fn to_value(&self) -> Value {
Value::Boolean(*self)
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
use bson::{oid::ObjectId, UtcDateTime};
use chrono::{DateTime, Utc};
@ -12,8 +12,8 @@ impl ScalarType for ObjectId {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_string().into())
fn to_value(&self) -> Value {
Value::String(self.to_string())
}
}
@ -23,7 +23,7 @@ impl ScalarType for UtcDateTime {
DateTime::<Utc>::parse(value).map(UtcDateTime::from)
}
fn to_json(&self) -> Result<serde_json::Value> {
(**self).to_json()
fn to_value(&self) -> Value {
(**self).to_value()
}
}

View File

@ -12,7 +12,7 @@ impl ScalarType for Tz {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(Tz::name(self).into())
fn to_value(&self) -> Value {
Value::String(Tz::name(self))
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
use chrono::{DateTime, TimeZone, Utc};
@ -14,7 +14,7 @@ impl ScalarType for DateTime<Utc> {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_rfc3339().into())
fn to_value(&self) -> Value {
Value::String(self.to_rfc3339())
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
macro_rules! impl_float_scalars {
@ -22,8 +22,8 @@ macro_rules! impl_float_scalars {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into())
fn to_value(&self) -> Value {
Value::Float(*self as f64)
}
}
)*

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
#[cfg(feature = "bson")]
use bson::oid::{self, ObjectId};
@ -89,7 +89,7 @@ impl ScalarType for ID {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.0.clone().into())
fn to_value(&self) -> Value {
Value::String(self.0.clone())
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
macro_rules! impl_integer_scalars {
@ -21,8 +21,8 @@ macro_rules! impl_integer_scalars {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into())
fn to_value(&self) -> Value {
Value::Int(*self as i32)
}
}
)*
@ -52,8 +52,8 @@ macro_rules! impl_int64_scalars {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_string().into())
fn to_value(&self) -> Value {
Value::String(self.to_string())
}
}
)*

View File

@ -1,4 +1,4 @@
use crate::{InputValueResult, Result, ScalarType, Value};
use crate::{InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
use serde::de::DeserializeOwned;
use serde::Serialize;
@ -29,8 +29,10 @@ impl<T: DeserializeOwned + Serialize + Send + Sync> ScalarType for Json<T> {
Ok(serde_json::from_value(value.into()).map(Json)?)
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(serde_json::to_value(&self.0).unwrap_or_else(|_| serde_json::Value::Null))
fn to_value(&self) -> Value {
serde_json::to_value(&self.0)
.unwrap_or_else(|_| serde_json::Value::Null)
.into()
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
use chrono::NaiveDate;
@ -11,7 +11,7 @@ impl ScalarType for NaiveDate {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.format("%Y-%m-%d").to_string().into())
fn to_value(&self) -> Value {
Value::String(self.format("%Y-%m-%d").to_string())
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
use chrono::NaiveTime;
@ -11,7 +11,7 @@ impl ScalarType for NaiveTime {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.format("%H:%M:%S").to_string().into())
fn to_value(&self) -> Value {
Value::String(self.format("%H:%M:%S").to_string())
}
}

View File

@ -23,8 +23,8 @@ impl ScalarType for String {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.clone().into())
fn to_value(&self) -> Value {
Value::String(self.clone())
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
use url::Url;
@ -11,7 +11,7 @@ impl ScalarType for Url {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_string().into())
fn to_value(&self) -> Value {
Value::String(self.to_string())
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use crate::{InputValueError, InputValueResult, ScalarType, Value};
use async_graphql_derive::Scalar;
use uuid::Uuid;
@ -11,7 +11,7 @@ impl ScalarType for Uuid {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_string().into())
fn to_value(&self) -> Value {
Value::String(self.to_string())
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value, ID};
use crate::{InputValueError, InputValueResult, ScalarType, Value, ID};
use async_graphql_derive::Scalar;
use std::ops::{Deref, DerefMut};
@ -54,7 +54,7 @@ impl ScalarType for Cursor {
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.0.to_string().into())
fn to_value(&self) -> Value {
Value::String(self.0.to_string())
}
}

View File

@ -1,4 +1,4 @@
use crate::{InputValueError, InputValueResult, Result, Type, Value};
use crate::{InputValueError, InputValueResult, Type, Value};
#[allow(missing_docs)]
pub struct EnumItem<T> {
@ -31,11 +31,11 @@ pub trait EnumType: Type + Sized + Eq + Send + Copy + Sized + 'static {
)))
}
fn resolve_enum(&self) -> Result<serde_json::Value> {
fn to_value(&self) -> Value {
let items = Self::items();
for item in items {
if item.value == *self {
return Ok(item.name.into());
return Value::Enum(item.name.into());
}
}
unreachable!()

View File

@ -33,6 +33,10 @@ impl<T: InputValueType> InputValueType for Vec<T> {
_ => Ok(vec![InputValueType::parse(value)?]),
}
}
fn to_value(&self) -> Value {
Value::List(self.iter().map(InputValueType::to_value).collect())
}
}
#[allow(clippy::ptr_arg)]

View File

@ -27,6 +27,13 @@ impl<T: InputValueType> InputValueType for Option<T> {
_ => Ok(Some(T::parse(value)?)),
}
}
fn to_value(&self) -> Value {
match self {
Some(value) => value.to_value(),
None => Value::Null,
}
}
}
#[async_trait::async_trait]

View File

@ -2,7 +2,7 @@ use crate::model::{__Schema, __Type};
use crate::scalars::Any;
use crate::{
do_resolve, registry, Context, ContextSelectionSet, Error, ObjectType, OutputValueType,
Positioned, QueryError, Result, Type, Value,
Positioned, QueryError, Result, Type,
};
use async_graphql_derive::SimpleObject;
use async_graphql_parser::query::Field;
@ -103,7 +103,7 @@ impl<T: ObjectType + Send + Sync> ObjectType for QueryRoot<T> {
)
.await;
} else if ctx.name.node == "__type" {
let type_name: String = ctx.param_value("name", || Value::Null)?;
let type_name: String = ctx.param_value("name", None)?;
let ctx_obj = ctx.with_selection_set(&ctx.selection_set);
return OutputValueType::resolve(
&ctx.schema_env
@ -116,7 +116,7 @@ impl<T: ObjectType + Send + Sync> ObjectType for QueryRoot<T> {
)
.await;
} else if ctx.name.node == "_entities" {
let representations: Vec<Any> = ctx.param_value("representations", || Value::Null)?;
let representations: Vec<Any> = ctx.param_value("representations", None)?;
let mut res = Vec::new();
for item in representations {
res.push(self.inner.find_entity(ctx, &item.0).await?);

View File

@ -86,4 +86,8 @@ impl<'a> InputValueType for Upload {
Err(InputValueError::ExpectedType(value))
}
}
fn to_value(&self) -> Value {
Value::Null
}
}

View File

@ -12,6 +12,15 @@ struct TestInput {
name: String,
}
impl Default for TestInput {
fn default() -> Self {
Self {
id: 423,
name: "foo".to_string(),
}
}
}
#[Enum(internal)]
enum DogCommand {
Sit,
@ -43,7 +52,7 @@ impl Dog {
unimplemented!()
}
async fn is_housetrained(&self, #[arg(default = "true")] at_other_homes: bool) -> Option<bool> {
async fn is_housetrained(&self, #[arg(default = true)] at_other_homes: bool) -> Option<bool> {
unimplemented!()
}
@ -245,8 +254,8 @@ impl ComplicatedArgs {
async fn multiple_opts(
&self,
#[arg(default = "0")] opt1: i32,
#[arg(default = "0")] opt2: i32,
#[arg(default)] opt1: i32,
#[arg(default)] opt2: i32,
) -> Option<String> {
unimplemented!()
}
@ -255,8 +264,8 @@ impl ComplicatedArgs {
&self,
req1: i32,
req2: i32,
#[arg(default = "0")] opt1: i32,
#[arg(default = "0")] opt2: i32,
#[arg(default)] opt1: i32,
#[arg(default)] opt2: i32,
) -> Option<String> {
unimplemented!()
}
@ -315,10 +324,7 @@ pub struct MutationRoot;
#[Object(internal)]
impl MutationRoot {
async fn test_input(
&self,
#[arg(default = r#"{id: 423, name: "foo"}"#)] input: TestInput,
) -> i32 {
async fn test_input(&self, #[arg(default)] input: TestInput) -> i32 {
unimplemented!()
}
}

View File

@ -4,10 +4,10 @@ use crate::Value;
/// Integer range validator
pub struct IntRange {
/// Minimum value, including this value.
pub min: i64,
pub min: i32,
/// Maximum value, including this value.
pub max: i64,
pub max: i32,
}
impl InputValueValidator for IntRange {
@ -30,7 +30,7 @@ impl InputValueValidator for IntRange {
/// Integer less then validator
pub struct IntLessThan {
/// Less then this value.
pub value: i64,
pub value: i32,
}
impl InputValueValidator for IntLessThan {
@ -53,7 +53,7 @@ impl InputValueValidator for IntLessThan {
/// Integer greater then validator
pub struct IntGreaterThan {
/// Greater then this value.
pub value: i64,
pub value: i32,
}
impl InputValueValidator for IntGreaterThan {
@ -93,7 +93,7 @@ impl InputValueValidator for IntNonZero {
/// Integer equal validator
pub struct IntEqual {
/// equal this value.
pub value: i64,
pub value: i32,
}
impl InputValueValidator for IntEqual {

View File

@ -6,7 +6,7 @@ pub async fn test_default_value_arg() {
#[Object]
impl Query {
async fn value(&self, #[arg(default = "100")] input: i32) -> i32 {
async fn value(&self, #[arg(default = 100)] input: i32) -> i32 {
input
}
}
@ -34,7 +34,7 @@ pub async fn test_default_value_arg() {
pub async fn test_default_value_inputobject() {
#[InputObject]
struct MyInput {
#[field(default = "100")]
#[field(default = 100)]
value: i32,
}

View File

@ -4,20 +4,20 @@ use async_graphql::*;
pub async fn test_input_object_default_value() {
#[InputObject]
struct MyInput {
#[field(default = "999")]
#[field(default = 999)]
a: i32,
#[field(default = "[1, 2, 3]")]
#[field(default_with = "vec![1, 2, 3]")]
b: Vec<i32>,
#[field(default = "\"abc\"")]
#[field(default = "abc")]
c: String,
#[field(default = "999")]
d: Option<i32>,
#[field(default = 999)]
d: i32,
#[field(default = "999")]
e: Option<i32>,
#[field(default = 999)]
e: i32,
}
struct MyOutput {
@ -60,8 +60,8 @@ pub async fn test_input_object_default_value() {
a: input.a,
b: input.b,
c: input.c,
d: input.d,
e: input.e,
d: Some(input.d),
e: Some(input.e),
}
}
}

View File

@ -83,8 +83,8 @@ impl ScalarType for TestScalar {
true
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.0.clone().into())
fn to_value(&self) -> Value {
Value::Int(self.0.clone())
}
}
@ -152,10 +152,7 @@ struct Subscription;
#[Subscription(desc = "Global subscription")]
impl Subscription {
/// simple_subscription description
async fn simple_subscription(
&self,
#[arg(default = "1")] step: i32,
) -> impl Stream<Item = i32> {
async fn simple_subscription(&self, #[arg(default = 1)] step: i32) -> impl Stream<Item = i32> {
stream::once(step)
}
}