Skip to content

Commit 6dd6abb

Browse files
authored
Fix custom ScalarValue usage for unions (#681)
- make GraphQLUnion trait generic over ScalarValue - generate generic over ScalarValue impls of GraphQLUnion trait - add codegen tests with a custom ScalarValue for union macros
1 parent 5b9c611 commit 6dd6abb

File tree

11 files changed

+176
-47
lines changed

11 files changed

+176
-47
lines changed

integration_tests/codegen_fail/fail/union/enum_non_object_variant.stderr

-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not satisfied
2-
--> $DIR/enum_non_object_variant.rs:9:10
3-
|
4-
9 | #[derive(GraphQLUnion)]
5-
| ^^^^^^^^^^^^ the trait `juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not implemented for `Test`
6-
|
7-
= note: required by `juniper::types::marker::GraphQLObjectType::mark`
8-
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
9-
101
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<__S>` is not satisfied
112
--> $DIR/enum_non_object_variant.rs:9:10
123
|

integration_tests/codegen_fail/fail/union/enum_same_type_ugly.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0119]: conflicting implementations of trait `<Character as juniper::types::marker::GraphQLUnion>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
1+
error[E0119]: conflicting implementations of trait `<Character as juniper::types::marker::GraphQLUnion<__S>>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
22
--> $DIR/enum_same_type_ugly.rs:3:10
33
|
44
3 | #[derive(GraphQLUnion)]

integration_tests/codegen_fail/fail/union/struct_non_object_variant.stderr

-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not satisfied
2-
--> $DIR/struct_non_object_variant.rs:9:10
3-
|
4-
9 | #[derive(GraphQLUnion)]
5-
| ^^^^^^^^^^^^ the trait `juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not implemented for `Test`
6-
|
7-
= note: required by `juniper::types::marker::GraphQLObjectType::mark`
8-
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
9-
101
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<__S>` is not satisfied
112
--> $DIR/struct_non_object_variant.rs:9:10
123
|

integration_tests/codegen_fail/fail/union/struct_same_type_ugly.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0119]: conflicting implementations of trait `<Character as juniper::types::marker::GraphQLUnion>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
1+
error[E0119]: conflicting implementations of trait `<Character as juniper::types::marker::GraphQLUnion<__S>>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
22
--> $DIR/struct_same_type_ugly.rs:3:10
33
|
44
3 | #[derive(GraphQLUnion)]

integration_tests/codegen_fail/fail/union/trait_non_object_variant.stderr

-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not satisfied
2-
--> $DIR/trait_non_object_variant.rs:9:1
3-
|
4-
9 | #[graphql_union]
5-
| ^^^^^^^^^^^^^^^^ the trait `juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not implemented for `Test`
6-
|
7-
= note: required by `juniper::types::marker::GraphQLObjectType::mark`
8-
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
9-
101
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<__S>` is not satisfied
112
--> $DIR/trait_non_object_variant.rs:9:1
123
|

integration_tests/codegen_fail/fail/union/trait_same_type_ugly.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0119]: conflicting implementations of trait `<(dyn Character + std::marker::Send + std::marker::Sync + '__obj) as juniper::types::marker::GraphQLUnion>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
1+
error[E0119]: conflicting implementations of trait `<(dyn Character + std::marker::Send + std::marker::Sync + '__obj) as juniper::types::marker::GraphQLUnion<__S>>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
22
--> $DIR/trait_same_type_ugly.rs:3:1
33
|
44
3 | #[graphql_union]

integration_tests/juniper_tests/src/codegen/union_attr.rs

+91
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,97 @@ mod explicit_scalar {
556556
}
557557
}
558558

559+
mod custom_scalar {
560+
use crate::custom_scalar::MyScalarValue;
561+
562+
use super::*;
563+
564+
#[graphql_union(scalar = MyScalarValue)]
565+
trait Character {
566+
fn as_human(&self) -> Option<&Human> {
567+
None
568+
}
569+
fn as_droid(&self) -> Option<&Droid> {
570+
None
571+
}
572+
}
573+
574+
impl Character for Human {
575+
fn as_human(&self) -> Option<&Human> {
576+
Some(&self)
577+
}
578+
}
579+
580+
impl Character for Droid {
581+
fn as_droid(&self) -> Option<&Droid> {
582+
Some(&self)
583+
}
584+
}
585+
586+
type DynCharacter<'a> = dyn Character + Send + Sync + 'a;
587+
588+
enum QueryRoot {
589+
Human,
590+
Droid,
591+
}
592+
593+
#[graphql_object(scalar = MyScalarValue)]
594+
impl QueryRoot {
595+
fn character(&self) -> Box<DynCharacter<'_>> {
596+
let ch: Box<DynCharacter<'_>> = match self {
597+
Self::Human => Box::new(Human {
598+
id: "human-32".to_string(),
599+
home_planet: "earth".to_string(),
600+
}),
601+
Self::Droid => Box::new(Droid {
602+
id: "droid-99".to_string(),
603+
primary_function: "run".to_string(),
604+
}),
605+
};
606+
ch
607+
}
608+
}
609+
610+
const DOC: &str = r#"{
611+
character {
612+
... on Human {
613+
humanId: id
614+
homePlanet
615+
}
616+
... on Droid {
617+
droidId: id
618+
primaryFunction
619+
}
620+
}
621+
}"#;
622+
623+
#[tokio::test]
624+
async fn resolves_human() {
625+
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human);
626+
627+
assert_eq!(
628+
execute(DOC, None, &schema, &Variables::new(), &()).await,
629+
Ok((
630+
graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
631+
vec![],
632+
)),
633+
);
634+
}
635+
636+
#[tokio::test]
637+
async fn resolves_droid() {
638+
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid);
639+
640+
assert_eq!(
641+
execute(DOC, None, &schema, &Variables::new(), &()).await,
642+
Ok((
643+
graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
644+
vec![],
645+
)),
646+
);
647+
}
648+
}
649+
559650
mod inferred_custom_context {
560651
use super::*;
561652

integration_tests/juniper_tests/src/codegen/union_derive.rs

+73
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,79 @@ mod explicit_scalar {
485485
}
486486
}
487487

488+
mod custom_scalar {
489+
use crate::custom_scalar::MyScalarValue;
490+
491+
use super::*;
492+
493+
#[derive(GraphQLUnion)]
494+
#[graphql(scalar = MyScalarValue)]
495+
enum Character {
496+
A(Human),
497+
B(Droid),
498+
}
499+
500+
enum QueryRoot {
501+
Human,
502+
Droid,
503+
}
504+
505+
#[graphql_object(scalar = MyScalarValue)]
506+
impl QueryRoot {
507+
fn character(&self) -> Character {
508+
match self {
509+
Self::Human => Character::A(Human {
510+
id: "human-32".to_string(),
511+
home_planet: "earth".to_string(),
512+
}),
513+
Self::Droid => Character::B(Droid {
514+
id: "droid-99".to_string(),
515+
primary_function: "run".to_string(),
516+
}),
517+
}
518+
}
519+
}
520+
521+
const DOC: &str = r#"{
522+
character {
523+
... on Human {
524+
humanId: id
525+
homePlanet
526+
}
527+
... on Droid {
528+
droidId: id
529+
primaryFunction
530+
}
531+
}
532+
}"#;
533+
534+
#[tokio::test]
535+
async fn resolves_human() {
536+
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human);
537+
538+
assert_eq!(
539+
execute(DOC, None, &schema, &Variables::new(), &()).await,
540+
Ok((
541+
graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
542+
vec![],
543+
)),
544+
);
545+
}
546+
547+
#[tokio::test]
548+
async fn resolves_droid() {
549+
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid);
550+
551+
assert_eq!(
552+
execute(DOC, None, &schema, &Variables::new(), &()).await,
553+
Ok((
554+
graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
555+
vec![],
556+
)),
557+
);
558+
}
559+
}
560+
488561
mod custom_context {
489562
use super::*;
490563

integration_tests/juniper_tests/src/custom_scalar.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use juniper::{
1010
use std::fmt;
1111

1212
#[derive(Debug, Clone, PartialEq, juniper::GraphQLScalarValue)]
13-
enum MyScalarValue {
13+
pub(crate) enum MyScalarValue {
1414
Int(i32),
1515
Long(i64),
1616
Float(f64),
@@ -59,7 +59,7 @@ impl ScalarValue for MyScalarValue {
5959
}
6060

6161
#[derive(Default, Debug)]
62-
struct MyScalarValueVisitor;
62+
pub(crate) struct MyScalarValueVisitor;
6363

6464
impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
6565
type Value = MyScalarValue;

juniper/src/types/marker.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub trait GraphQLObjectType<S: ScalarValue>: GraphQLType<S> {
3737
/// [4]: https://spec.graphql.org/June2018/#sec-Objects
3838
/// [5]: https://spec.graphql.org/June2018/#sec-Input-Objects
3939
/// [6]: https://spec.graphql.org/June2018/#sec-Interfaces
40-
pub trait GraphQLUnion: GraphQLType {
40+
pub trait GraphQLUnion<S: ScalarValue>: GraphQLType<S> {
4141
/// An arbitrary function without meaning.
4242
///
4343
/// May contain compile timed check logic which ensures that types are used correctly according

juniper_codegen/src/graphql_union/mod.rs

+6-14
Original file line numberDiff line numberDiff line change
@@ -414,11 +414,6 @@ impl ToTokens for UnionDefinition {
414414
.as_ref()
415415
.map(|scl| quote! { #scl })
416416
.unwrap_or_else(|| quote! { __S });
417-
let default_scalar = self
418-
.scalar
419-
.as_ref()
420-
.map(|scl| quote! { #scl })
421-
.unwrap_or_else(|| quote! { #crate_path::DefaultScalarValue });
422417

423418
let description = self
424419
.description
@@ -493,13 +488,10 @@ impl ToTokens for UnionDefinition {
493488

494489
let (_, ty_generics, _) = self.generics.split_for_impl();
495490

496-
let mut base_generics = self.generics.clone();
491+
let mut ext_generics = self.generics.clone();
497492
if self.is_trait_object {
498-
base_generics.params.push(parse_quote! { '__obj });
493+
ext_generics.params.push(parse_quote! { '__obj });
499494
}
500-
let (impl_generics, _, _) = base_generics.split_for_impl();
501-
502-
let mut ext_generics = base_generics.clone();
503495
if self.scalar.is_none() {
504496
ext_generics.params.push(parse_quote! { #scalar });
505497
ext_generics
@@ -618,13 +610,13 @@ impl ToTokens for UnionDefinition {
618610

619611
let union_impl = quote! {
620612
#[automatically_derived]
621-
impl#impl_generics #crate_path::marker::GraphQLUnion for #ty_full {
613+
impl#ext_impl_generics #crate_path::marker::GraphQLUnion<#scalar> for #ty_full
614+
#where_clause
615+
{
622616
fn mark() {
623617
#all_variants_unique
624618

625-
#( <#var_types as #crate_path::marker::GraphQLObjectType<
626-
#default_scalar,
627-
>>::mark(); )*
619+
#( <#var_types as #crate_path::marker::GraphQLObjectType<#scalar>>::mark(); )*
628620
}
629621
}
630622
};

0 commit comments

Comments
 (0)