From cb6d89f4c7f637e8840fde11dc141bcab36f9b22 Mon Sep 17 00:00:00 2001 From: Jonas Meurer Date: Mon, 16 Nov 2020 04:15:55 +0100 Subject: [PATCH] Allow different Scalar for GraphQLScalarValue (#807) * allow setting scalar in macro * rustfmt * added changes to changelog * added test cases --- .../src/codegen/derive_scalar.rs | 139 ++++++++++++++++++ .../juniper_tests/src/codegen/mod.rs | 1 + juniper/CHANGELOG.md | 2 + juniper_codegen/src/derive_scalar_value.rs | 87 +++++++---- 4 files changed, 197 insertions(+), 32 deletions(-) create mode 100644 integration_tests/juniper_tests/src/codegen/derive_scalar.rs diff --git a/integration_tests/juniper_tests/src/codegen/derive_scalar.rs b/integration_tests/juniper_tests/src/codegen/derive_scalar.rs new file mode 100644 index 00000000..6ed5f29d --- /dev/null +++ b/integration_tests/juniper_tests/src/codegen/derive_scalar.rs @@ -0,0 +1,139 @@ +use crate::custom_scalar::MyScalarValue; +use juniper::{ + execute, EmptyMutation, EmptySubscription, FromInputValue, InputValue, RootNode, ToInputValue, + Value, Variables, +}; + +#[derive(Debug, PartialEq, Eq, Hash, juniper::GraphQLScalarValue)] +#[graphql(transparent, scalar = MyScalarValue)] +pub struct LargeId(i64); + +#[derive(juniper::GraphQLObject)] +#[graphql(scalar = MyScalarValue)] +struct User { + id: LargeId, +} + +struct Query; + +#[juniper::graphql_object(scalar = MyScalarValue)] +impl Query { + fn user() -> User { + User { id: LargeId(0) } + } +} + +struct Mutation; + +#[juniper::graphql_object(scalar = MyScalarValue)] +impl Mutation { + fn change_user(id: LargeId) -> User { + User { id } + } +} + +#[test] +fn test_scalar_value_large_id() { + let num: i64 = 4294967297; + + let input_integer: InputValue = + serde_json::from_value(serde_json::json!(num)).unwrap(); + + let output: LargeId = + FromInputValue::::from_input_value(&input_integer).unwrap(); + assert_eq!(output, LargeId(num)); + + let id = LargeId(num); + let output = ToInputValue::::to_input_value(&id); + assert_eq!(output, InputValue::scalar(num)); +} + +#[tokio::test] +async fn test_scalar_value_large_query() { + let schema = RootNode::<'_, _, _, _, MyScalarValue>::new_with_scalar_value( + Query, + EmptyMutation::<()>::new(), + EmptySubscription::<()>::new(), + ); + + let doc = r#" + query { + user { id } + }"#; + + assert_eq!( + execute(doc, None, &schema, &Variables::::new(), &()).await, + Ok(( + Value::object( + vec![( + "user", + Value::object( + vec![("id", Value::::scalar(0_i64)),] + .into_iter() + .collect(), + ), + )] + .into_iter() + .collect() + ), + vec![] + )) + ); +} + +#[tokio::test] +async fn test_scalar_value_large_mutation() { + let schema = RootNode::<'_, _, _, _, MyScalarValue>::new_with_scalar_value( + Query, + Mutation, + EmptySubscription::<()>::new(), + ); + + let doc = r#" + mutation { + changeUser(id: 1) { id } + }"#; + + assert_eq!( + execute(doc, None, &schema, &Variables::::new(), &()).await, + Ok(( + Value::object( + vec![( + "changeUser", + Value::object( + vec![("id", Value::::scalar(1_i64)),] + .into_iter() + .collect(), + ), + )] + .into_iter() + .collect() + ), + vec![] + )) + ); + + let doc = r#" + mutation { + changeUser(id: 4294967297) { id } + }"#; + + assert_eq!( + execute(doc, None, &schema, &Variables::::new(), &()).await, + Ok(( + Value::object( + vec![( + "changeUser", + Value::object( + vec![("id", Value::::scalar(4294967297_i64)),] + .into_iter() + .collect(), + ), + )] + .into_iter() + .collect() + ), + vec![] + )) + ); +} diff --git a/integration_tests/juniper_tests/src/codegen/mod.rs b/integration_tests/juniper_tests/src/codegen/mod.rs index 37e12c31..0b45de8d 100644 --- a/integration_tests/juniper_tests/src/codegen/mod.rs +++ b/integration_tests/juniper_tests/src/codegen/mod.rs @@ -2,6 +2,7 @@ mod derive_enum; mod derive_input_object; mod derive_object; mod derive_object_with_raw_idents; +mod derive_scalar; mod impl_object; mod impl_scalar; mod interface_attr; diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 1da87aff..6e60bda0 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -39,6 +39,8 @@ - Implement `IntoFieldError` for `std::convert::Infallible`. ([#796](https://github.com/graphql-rust/juniper/pull/796)) +- Allow using `#[graphql(Scalar = DefaultScalarValue)]` for derive macro `GraphQLScalarValue` ([#807](https://github.com/graphql-rust/juniper/pull/807)) + ## Fixes - Massively improved the `#[graphql_union]` proc macro. ([#666](https://github.com/graphql-rust/juniper/pull/666)): diff --git a/juniper_codegen/src/derive_scalar_value.rs b/juniper_codegen/src/derive_scalar_value.rs index b725cfa1..9a9fc466 100644 --- a/juniper_codegen/src/derive_scalar_value.rs +++ b/juniper_codegen/src/derive_scalar_value.rs @@ -12,6 +12,7 @@ struct TransparentAttributes { transparent: Option, name: Option, description: Option, + scalar: Option, } impl syn::parse::Parse for TransparentAttributes { @@ -20,6 +21,7 @@ impl syn::parse::Parse for TransparentAttributes { transparent: None, name: None, description: None, + scalar: None, }; while !input.is_empty() { @@ -38,6 +40,11 @@ impl syn::parse::Parse for TransparentAttributes { "transparent" => { output.transparent = Some(true); } + "scalar" | "Scalar" => { + input.parse::()?; + let val = input.parse::()?; + output.scalar = Some(val); + } _ => return Err(syn::Error::new(ident.span(), "unknown attribute")), } input.try_parse::()?; @@ -99,22 +106,34 @@ fn impl_scalar_struct( None => quote!(), }; + let scalar = attrs + .scalar + .as_ref() + .map(|s| quote!( #s )) + .unwrap_or_else(|| quote!(__S)); + + let impl_generics = attrs + .scalar + .as_ref() + .map(|_| quote!()) + .unwrap_or_else(|| quote!(<__S>)); + let _async = quote!( - impl<__S> ::juniper::GraphQLValueAsync<__S> for #ident + impl#impl_generics ::juniper::GraphQLValueAsync<#scalar> for #ident where Self: Sync, Self::TypeInfo: Sync, Self::Context: Sync, - __S: ::juniper::ScalarValue + Send + Sync, + #scalar: ::juniper::ScalarValue + Send + Sync, { fn resolve_async<'a>( &'a self, info: &'a Self::TypeInfo, - selection_set: Option<&'a [::juniper::Selection<__S>]>, - executor: &'a ::juniper::Executor, - ) -> ::juniper::BoxFuture<'a, ::juniper::ExecutionResult<__S>> { + selection_set: Option<&'a [::juniper::Selection<#scalar>]>, + executor: &'a ::juniper::Executor, + ) -> ::juniper::BoxFuture<'a, ::juniper::ExecutionResult<#scalar>> { use ::juniper::futures::future; - let v = ::juniper::GraphQLValue::resolve(self, info, selection_set, executor); + let v = ::juniper::GraphQLValue::<#scalar>::resolve(self, info, selection_set, executor); Box::pin(future::ready(v)) } } @@ -123,9 +142,9 @@ fn impl_scalar_struct( let content = quote!( #_async - impl ::juniper::GraphQLType for #ident + impl#impl_generics ::juniper::GraphQLType<#scalar> for #ident where - S: ::juniper::ScalarValue, + #scalar: ::juniper::ScalarValue, { fn name(_: &Self::TypeInfo) -> Option<&'static str> { Some(#name) @@ -133,10 +152,10 @@ fn impl_scalar_struct( fn meta<'r>( info: &Self::TypeInfo, - registry: &mut ::juniper::Registry<'r, S>, - ) -> ::juniper::meta::MetaType<'r, S> + registry: &mut ::juniper::Registry<'r, #scalar>, + ) -> ::juniper::meta::MetaType<'r, #scalar> where - S: 'r, + #scalar: 'r, { registry.build_scalar_type::(info) #description @@ -144,59 +163,63 @@ fn impl_scalar_struct( } } - impl ::juniper::GraphQLValue for #ident + impl#impl_generics ::juniper::GraphQLValue<#scalar> for #ident where - S: ::juniper::ScalarValue, + #scalar: ::juniper::ScalarValue, { type Context = (); type TypeInfo = (); fn type_name<'__i>(&self, info: &'__i Self::TypeInfo) -> Option<&'__i str> { - >::name(info) + >::name(info) } fn resolve( &self, info: &(), - selection: Option<&[::juniper::Selection]>, - executor: &::juniper::Executor, - ) -> ::juniper::ExecutionResult { - ::juniper::GraphQLValue::resolve(&self.0, info, selection, executor) + selection: Option<&[::juniper::Selection<#scalar>]>, + executor: &::juniper::Executor, + ) -> ::juniper::ExecutionResult<#scalar> { + ::juniper::GraphQLValue::<#scalar>::resolve(&self.0, info, selection, executor) } } - impl ::juniper::ToInputValue for #ident + impl#impl_generics ::juniper::ToInputValue<#scalar> for #ident where - S: ::juniper::ScalarValue, + #scalar: ::juniper::ScalarValue, { - fn to_input_value(&self) -> ::juniper::InputValue { - ::juniper::ToInputValue::to_input_value(&self.0) + fn to_input_value(&self) -> ::juniper::InputValue<#scalar> { + ::juniper::ToInputValue::<#scalar>::to_input_value(&self.0) } } - impl ::juniper::FromInputValue for #ident + impl#impl_generics ::juniper::FromInputValue<#scalar> for #ident where - S: ::juniper::ScalarValue, + #scalar: ::juniper::ScalarValue, { - fn from_input_value(v: &::juniper::InputValue) -> Option<#ident> { - let inner: #inner_ty = ::juniper::FromInputValue::from_input_value(v)?; + fn from_input_value(v: &::juniper::InputValue<#scalar>) -> Option<#ident> { + let inner: #inner_ty = ::juniper::FromInputValue::<#scalar>::from_input_value(v)?; Some(#ident(inner)) } } - impl ::juniper::ParseScalarValue for #ident + impl#impl_generics ::juniper::ParseScalarValue<#scalar> for #ident where - S: ::juniper::ScalarValue, + #scalar: ::juniper::ScalarValue, { fn from_str<'a>( value: ::juniper::parser::ScalarToken<'a>, - ) -> ::juniper::ParseScalarResult<'a, S> { - <#inner_ty as ::juniper::ParseScalarValue>::from_str(value) + ) -> ::juniper::ParseScalarResult<'a, #scalar> { + <#inner_ty as ::juniper::ParseScalarValue<#scalar>>::from_str(value) } } - impl ::juniper::marker::IsOutputType for #ident { } - impl ::juniper::marker::IsInputType for #ident { } + impl#impl_generics ::juniper::marker::IsOutputType<#scalar> for #ident + where #scalar: ::juniper::ScalarValue, + { } + impl#impl_generics ::juniper::marker::IsInputType<#scalar> for #ident + where #scalar: ::juniper::ScalarValue, + { } ); Ok(content)