Derive macro for tagged enums (GraphQLUnion) (#618)
* Implemented device macro for GraphQLUnion's * Updated PR link in CHNAGELOG * Disabled documentation on enumeration fields * Disabled skip on fields * Changed implementation for std::convert::Into since skip is not possible * Added documentation for GraphQLUnion * Added tests for GraphQLUnion * Fixed typos in error messages (as suggested by review) * Fixed failing documentation example * Utilized `resolver_code` in `util::GraphQLTypeDefinitionField`. Simplifies code and provides the idea of using `util::GraphQLTypeDefinitionField` for different types than objects. * Removed wrong statement about skip annotation in docs. Co-authored-by: Christian Legnitto <LegNeato@users.noreply.github.com>
This commit is contained in:
parent
47f7ffaa5b
commit
a05f4e55c4
8 changed files with 645 additions and 6 deletions
|
@ -3,10 +3,11 @@
|
|||
From a server's point of view, GraphQL unions are similar to interfaces: the
|
||||
only exception is that they don't contain fields on their own.
|
||||
|
||||
In Juniper, the `graphql_union!` has identical syntax to the [interface
|
||||
macro](interfaces.md), but does not support defining fields. Therefore, the same
|
||||
considerations about using traits, placeholder types, or enums still apply to
|
||||
unions.
|
||||
In Juniper, the `graphql_union!` has identical syntax to the
|
||||
[interface macro](interfaces.md), but does not support defining
|
||||
fields. Therefore, the same considerations about using traits,
|
||||
placeholder types, or enums still apply to unions. For simple
|
||||
situations, Juniper provides `#[derive(GraphQLUnion)]` for enums.
|
||||
|
||||
If we look at the same examples as in the interfaces chapter, we see the
|
||||
similarities and the tradeoffs:
|
||||
|
@ -154,7 +155,7 @@ impl GraphQLUnion for Character {
|
|||
# fn main() {}
|
||||
```
|
||||
|
||||
## Enums
|
||||
## Enums (Impl)
|
||||
|
||||
```rust
|
||||
#[derive(juniper::GraphQLObject)]
|
||||
|
@ -187,3 +188,32 @@ impl Character {
|
|||
|
||||
# fn main() {}
|
||||
```
|
||||
|
||||
## Enums (Derive)
|
||||
|
||||
This example is similar to `Enums (Impl)`. To successfully use the
|
||||
derive macro, ensure that each variant of the enum has a different
|
||||
type. Since each variant is different, the device macro provides
|
||||
`std::convert::Into<T>` converter for each variant.
|
||||
|
||||
```rust
|
||||
#[derive(juniper::GraphQLObject)]
|
||||
struct Human {
|
||||
id: String,
|
||||
home_planet: String,
|
||||
}
|
||||
|
||||
#[derive(juniper::GraphQLObject)]
|
||||
struct Droid {
|
||||
id: String,
|
||||
primary_function: String,
|
||||
}
|
||||
|
||||
#[derive(juniper::GraphQLUnion)]
|
||||
enum Character {
|
||||
Human(Human),
|
||||
Droid(Droid),
|
||||
}
|
||||
|
||||
# fn main() {}
|
||||
```
|
||||
|
|
268
integration_tests/juniper_tests/src/codegen/derive_union.rs
Normal file
268
integration_tests/juniper_tests/src/codegen/derive_union.rs
Normal file
|
@ -0,0 +1,268 @@
|
|||
// Test for union's derive macro
|
||||
|
||||
#[cfg(test)]
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
#[cfg(test)]
|
||||
use juniper::{
|
||||
self, execute, DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLType, RootNode,
|
||||
Value, Variables,
|
||||
};
|
||||
|
||||
#[derive(juniper::GraphQLObject)]
|
||||
pub struct Human {
|
||||
id: String,
|
||||
home_planet: String,
|
||||
}
|
||||
|
||||
#[derive(juniper::GraphQLObject)]
|
||||
pub struct Droid {
|
||||
id: String,
|
||||
primary_function: String,
|
||||
}
|
||||
|
||||
#[derive(juniper::GraphQLUnion)]
|
||||
#[graphql(description = "A Collection of things")]
|
||||
pub enum Character {
|
||||
One(Human),
|
||||
Two(Droid),
|
||||
}
|
||||
|
||||
// Context Test
|
||||
pub struct CustomContext {
|
||||
is_left: bool,
|
||||
}
|
||||
|
||||
impl juniper::Context for CustomContext {}
|
||||
|
||||
#[derive(juniper::GraphQLObject)]
|
||||
#[graphql(Context = CustomContext)]
|
||||
pub struct HumanContext {
|
||||
id: String,
|
||||
home_planet: String,
|
||||
}
|
||||
|
||||
#[derive(juniper::GraphQLObject)]
|
||||
#[graphql(Context = CustomContext)]
|
||||
pub struct DroidContext {
|
||||
id: String,
|
||||
primary_function: String,
|
||||
}
|
||||
|
||||
/// A Collection of things
|
||||
#[derive(juniper::GraphQLUnion)]
|
||||
#[graphql(Context = CustomContext)]
|
||||
pub enum CharacterContext {
|
||||
One(HumanContext),
|
||||
Two(DroidContext),
|
||||
}
|
||||
|
||||
// #[juniper::object] compatibility
|
||||
|
||||
pub struct HumanCompat {
|
||||
id: String,
|
||||
home_planet: String,
|
||||
}
|
||||
|
||||
#[juniper::graphql_object]
|
||||
impl HumanCompat {
|
||||
fn id(&self) -> &String {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn home_planet(&self) -> &String {
|
||||
&self.home_planet
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DroidCompat {
|
||||
id: String,
|
||||
primary_function: String,
|
||||
}
|
||||
|
||||
#[juniper::graphql_object]
|
||||
impl DroidCompat {
|
||||
fn id(&self) -> &String {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn primary_function(&self) -> &String {
|
||||
&self.primary_function
|
||||
}
|
||||
}
|
||||
|
||||
// NOTICE: this can not compile due to generic implementation of GraphQLType<__S>
|
||||
// #[derive(juniper::GraphQLUnion)]
|
||||
// pub enum CharacterCompatFail {
|
||||
// One(HumanCompat),
|
||||
// Two(DroidCompat),
|
||||
// }
|
||||
|
||||
/// A Collection of things
|
||||
#[derive(juniper::GraphQLUnion)]
|
||||
#[graphql(Scalar = juniper::DefaultScalarValue)]
|
||||
pub enum CharacterCompat {
|
||||
One(HumanCompat),
|
||||
Two(DroidCompat),
|
||||
}
|
||||
|
||||
pub struct Query;
|
||||
|
||||
#[juniper::graphql_object(
|
||||
Context = CustomContext,
|
||||
)]
|
||||
impl Query {
|
||||
fn context(&self, ctx: &CustomContext) -> CharacterContext {
|
||||
if ctx.is_left {
|
||||
HumanContext {
|
||||
id: "human-32".to_string(),
|
||||
home_planet: "earth".to_string(),
|
||||
}
|
||||
.into()
|
||||
} else {
|
||||
DroidContext {
|
||||
id: "droid-99".to_string(),
|
||||
primary_function: "run".to_string(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_derived_union_doc_macro() {
|
||||
assert_eq!(
|
||||
<Character as GraphQLType<DefaultScalarValue>>::name(&()),
|
||||
Some("Character")
|
||||
);
|
||||
|
||||
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default());
|
||||
let meta = Character::meta(&(), &mut registry);
|
||||
|
||||
assert_eq!(meta.name(), Some("Character"));
|
||||
assert_eq!(
|
||||
meta.description(),
|
||||
Some(&"A Collection of things".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_derived_union_doc_string() {
|
||||
assert_eq!(
|
||||
<CharacterContext as GraphQLType<DefaultScalarValue>>::name(&()),
|
||||
Some("CharacterContext")
|
||||
);
|
||||
|
||||
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default());
|
||||
let meta = CharacterContext::meta(&(), &mut registry);
|
||||
|
||||
assert_eq!(meta.name(), Some("CharacterContext"));
|
||||
assert_eq!(
|
||||
meta.description(),
|
||||
Some(&"A Collection of things".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_derived_union_left() {
|
||||
let doc = r#"
|
||||
{
|
||||
context {
|
||||
... on HumanContext {
|
||||
humanId: id
|
||||
homePlanet
|
||||
}
|
||||
... on DroidContext {
|
||||
droidId: id
|
||||
primaryFunction
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
|
||||
let schema = RootNode::new(
|
||||
Query,
|
||||
EmptyMutation::<CustomContext>::new(),
|
||||
EmptySubscription::<CustomContext>::new(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
execute(
|
||||
doc,
|
||||
None,
|
||||
&schema,
|
||||
&Variables::new(),
|
||||
&CustomContext { is_left: true }
|
||||
)
|
||||
.await,
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![(
|
||||
"context",
|
||||
Value::object(
|
||||
vec![
|
||||
("humanId", Value::scalar("human-32".to_string())),
|
||||
("homePlanet", Value::scalar("earth".to_string())),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_derived_union_right() {
|
||||
let doc = r#"
|
||||
{
|
||||
context {
|
||||
... on HumanContext {
|
||||
humanId: id
|
||||
homePlanet
|
||||
}
|
||||
... on DroidContext {
|
||||
droidId: id
|
||||
primaryFunction
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
|
||||
let schema = RootNode::new(
|
||||
Query,
|
||||
EmptyMutation::<CustomContext>::new(),
|
||||
EmptySubscription::<CustomContext>::new(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
execute(
|
||||
doc,
|
||||
None,
|
||||
&schema,
|
||||
&Variables::new(),
|
||||
&CustomContext { is_left: false }
|
||||
)
|
||||
.await,
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![(
|
||||
"context",
|
||||
Value::object(
|
||||
vec![
|
||||
("droidId", Value::scalar("droid-99".to_string())),
|
||||
("primaryFunction", Value::scalar("run".to_string())),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
|
@ -2,5 +2,6 @@ mod derive_enum;
|
|||
mod derive_input_object;
|
||||
mod derive_object;
|
||||
mod derive_object_with_raw_idents;
|
||||
mod derive_union;
|
||||
mod impl_union;
|
||||
mod scalar_value_transparent;
|
||||
|
|
|
@ -22,6 +22,11 @@ See [#419](https://github.com/graphql-rust/juniper/pull/419).
|
|||
|
||||
See [#569](https://github.com/graphql-rust/juniper/pull/569).
|
||||
|
||||
- GraphQLUnion derive support ("#[derive(GraphqQLUnion)]")
|
||||
- implements GraphQLAsyncType
|
||||
|
||||
See [#618](https://github.com/graphql-rust/juniper/pull/618).
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- `juniper::graphiql` has moved to `juniper::http::graphiql`
|
||||
|
|
|
@ -116,7 +116,7 @@ extern crate bson;
|
|||
// functionality automatically.
|
||||
pub use juniper_codegen::{
|
||||
graphql_object, graphql_subscription, graphql_union, GraphQLEnum, GraphQLInputObject,
|
||||
GraphQLObject, GraphQLScalarValue,
|
||||
GraphQLObject, GraphQLScalarValue, GraphQLUnion,
|
||||
};
|
||||
// Internal macros are not exported,
|
||||
// but declared at the root to make them easier to use.
|
||||
|
|
122
juniper_codegen/src/derive_union.rs
Normal file
122
juniper_codegen/src/derive_union.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{self, Data, Fields};
|
||||
|
||||
use crate::util;
|
||||
|
||||
pub fn build_derive_union(ast: syn::DeriveInput, is_internal: bool) -> TokenStream {
|
||||
let enum_fields = match ast.data {
|
||||
Data::Enum(data) => data.variants,
|
||||
_ => {
|
||||
panic!("#[derive(GraphQLUnion)] can only be applied to enums");
|
||||
}
|
||||
};
|
||||
|
||||
// Parse attributes.
|
||||
let attrs = match util::ObjectAttributes::from_attrs(&ast.attrs) {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
panic!("Invalid #[graphql(...)] attribute for enum: {}", e);
|
||||
}
|
||||
};
|
||||
|
||||
if !attrs.interfaces.is_empty() {
|
||||
panic!("#[derive(GraphQLUnion)] does not support interfaces");
|
||||
}
|
||||
|
||||
let ident = &ast.ident;
|
||||
let name = attrs.name.unwrap_or_else(|| ident.to_string());
|
||||
|
||||
let fields = enum_fields.into_iter().filter_map(|field| {
|
||||
let field_attrs = match util::FieldAttributes::from_attrs(
|
||||
field.attrs,
|
||||
util::FieldAttributeParseMode::Object,
|
||||
) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => panic!("Invalid #[graphql] attribute for field: \n{}", e),
|
||||
};
|
||||
|
||||
|
||||
if field_attrs.skip {
|
||||
panic!("#[derive(GraphQLUnion)] does not support #[graphql(skip)] on fields");
|
||||
} else {
|
||||
let variant_name = field.ident;
|
||||
let name = field_attrs
|
||||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| util::to_camel_case(&variant_name.to_string()));
|
||||
|
||||
let resolver_code = quote!(
|
||||
#ident :: #variant_name
|
||||
);
|
||||
|
||||
let _type = match field.fields {
|
||||
Fields::Unnamed(inner) => {
|
||||
let mut iter = inner.unnamed.iter();
|
||||
let first = match iter.next() {
|
||||
Some(val) => val,
|
||||
None => unreachable!(),
|
||||
};
|
||||
|
||||
if iter.next().is_some() {
|
||||
panic!("#[derive(GraphQLUnion)] all members must be unnamed with a single element e.g. Some(T)");
|
||||
}
|
||||
|
||||
first.ty.clone()
|
||||
}
|
||||
_ => panic!("#[derive(GraphQLUnion)] all fields of the enum must be unnamed"),
|
||||
};
|
||||
|
||||
if field_attrs.description.is_some() {
|
||||
panic!("#[derive(GraphQLUnion)] does not allow documentation of fields");
|
||||
}
|
||||
|
||||
Some(util::GraphQLTypeDefinitionField {
|
||||
name,
|
||||
_type,
|
||||
args: Vec::new(),
|
||||
description: None,
|
||||
deprecation: field_attrs.deprecation,
|
||||
resolver_code,
|
||||
is_type_inferred: true,
|
||||
is_async: false,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let fields = fields.collect::<Vec<_>>();
|
||||
|
||||
// NOTICE: This is not an optimal implementation. It is possible
|
||||
// to bypass this check by using a full qualified path instead
|
||||
// (crate::Test vs Test). Since this requirement is mandatory, the
|
||||
// `std::convert::Into<T>` implementation is used to enforce this
|
||||
// requirement. However, due to the bad error message this
|
||||
// implementation should stay and provide guidance.
|
||||
let all_variants_different = {
|
||||
let mut all_types: Vec<_> = fields.iter().map(|field| &field._type).collect();
|
||||
let before = all_types.len();
|
||||
all_types.dedup();
|
||||
before == all_types.len()
|
||||
};
|
||||
|
||||
if !all_variants_different {
|
||||
panic!("#[derive(GraphQLUnion)] each variant must have a different type");
|
||||
}
|
||||
|
||||
let definition = util::GraphQLTypeDefiniton {
|
||||
name,
|
||||
_type: syn::parse_str(&ast.ident.to_string()).unwrap(),
|
||||
context: attrs.context,
|
||||
scalar: attrs.scalar,
|
||||
description: attrs.description,
|
||||
fields,
|
||||
generics: ast.generics,
|
||||
interfaces: None,
|
||||
include_type_generics: true,
|
||||
generic_scalar: true,
|
||||
no_async: attrs.no_async,
|
||||
};
|
||||
|
||||
let juniper_crate_name = if is_internal { "crate" } else { "juniper" };
|
||||
definition.into_union_tokens(juniper_crate_name)
|
||||
}
|
|
@ -15,6 +15,7 @@ mod derive_enum;
|
|||
mod derive_input_object;
|
||||
mod derive_object;
|
||||
mod derive_scalar_value;
|
||||
mod derive_union;
|
||||
mod impl_object;
|
||||
mod impl_union;
|
||||
|
||||
|
@ -63,6 +64,13 @@ pub fn derive_object_internal(input: TokenStream) -> TokenStream {
|
|||
let gen = derive_object::build_derive_object(ast, true);
|
||||
gen.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(GraphQLUnion, attributes(graphql))]
|
||||
pub fn derive_union(input: TokenStream) -> TokenStream {
|
||||
let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
|
||||
let gen = derive_union::build_derive_union(ast, false);
|
||||
gen.into()
|
||||
}
|
||||
/// This custom derive macro implements the #[derive(GraphQLScalarValue)]
|
||||
/// derive.
|
||||
///
|
||||
|
|
|
@ -1255,6 +1255,211 @@ impl GraphQLTypeDefiniton {
|
|||
#subscription_implementation
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_union_tokens(self, juniper_crate_name: &str) -> proc_macro2::TokenStream {
|
||||
let juniper_crate_name = syn::parse_str::<syn::Path>(juniper_crate_name).unwrap();
|
||||
|
||||
let name = &self.name;
|
||||
let ty = &self._type;
|
||||
let context = self
|
||||
.context
|
||||
.as_ref()
|
||||
.map(|ctx| quote!( #ctx ))
|
||||
.unwrap_or_else(|| quote!(()));
|
||||
|
||||
let scalar = self
|
||||
.scalar
|
||||
.as_ref()
|
||||
.map(|s| quote!( #s ))
|
||||
.unwrap_or_else(|| {
|
||||
if self.generic_scalar {
|
||||
// If generic_scalar is true, we always insert a generic scalar.
|
||||
// See more comments below.
|
||||
quote!(__S)
|
||||
} else {
|
||||
quote!(#juniper_crate_name::DefaultScalarValue)
|
||||
}
|
||||
});
|
||||
|
||||
let description = self
|
||||
.description
|
||||
.as_ref()
|
||||
.map(|description| quote!( .description(#description) ));
|
||||
|
||||
let meta_types = self.fields.iter().map(|field| {
|
||||
let var_ty = &field._type;
|
||||
|
||||
quote! {
|
||||
registry.get_type::<&#var_ty>(&(())),
|
||||
}
|
||||
});
|
||||
|
||||
let matcher_variants = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let var_ty = &field._type;
|
||||
let resolver_code = &field.resolver_code;
|
||||
|
||||
quote!(
|
||||
#resolver_code(ref x) => <#var_ty as #juniper_crate_name::GraphQLType<#scalar>>::name(&()).unwrap().to_string(),
|
||||
)
|
||||
});
|
||||
|
||||
let concrete_type_resolver = quote!(
|
||||
match self {
|
||||
#( #matcher_variants )*
|
||||
}
|
||||
);
|
||||
|
||||
let matcher_expr: Vec<_> = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let resolver_code = &field.resolver_code;
|
||||
|
||||
quote!(
|
||||
match self { #resolver_code(ref val) => Some(val), _ => None, }
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let resolve_into_type = self.fields.iter().zip(matcher_expr.iter()).map(|(field, expr)| {
|
||||
let var_ty = &field._type;
|
||||
|
||||
quote! {
|
||||
if type_name == (<#var_ty as #juniper_crate_name::GraphQLType<#scalar>>::name(&())).unwrap() {
|
||||
return executor.resolve(&(), &{ #expr });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let resolve_into_type_async = self.fields.iter().zip(matcher_expr.iter()).map(|(field, expr)| {
|
||||
let var_ty = &field._type;
|
||||
|
||||
quote! {
|
||||
if type_name == (<#var_ty as #juniper_crate_name::GraphQLType<#scalar>>::name(&())).unwrap() {
|
||||
let f = async move {
|
||||
executor.resolve_async(&(), &{ #expr }).await
|
||||
};
|
||||
use futures::future;
|
||||
return future::FutureExt::boxed(f);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut generics = self.generics.clone();
|
||||
|
||||
if self.scalar.is_none() && self.generic_scalar {
|
||||
// No custom scalar specified, but always generic specified.
|
||||
// Therefore we inject the generic scalar.
|
||||
|
||||
generics.params.push(parse_quote!(__S));
|
||||
|
||||
let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
|
||||
// Insert ScalarValue constraint.
|
||||
where_clause
|
||||
.predicates
|
||||
.push(parse_quote!(__S: #juniper_crate_name::ScalarValue));
|
||||
}
|
||||
|
||||
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
let mut where_async = where_clause.cloned().unwrap_or_else(|| parse_quote!(where));
|
||||
where_async
|
||||
.predicates
|
||||
.push(parse_quote!( #scalar: Send + Sync ));
|
||||
where_async.predicates.push(parse_quote!(Self: Send + Sync));
|
||||
|
||||
let async_type_impl = quote!(
|
||||
impl#impl_generics #juniper_crate_name::GraphQLTypeAsync<#scalar> for #ty
|
||||
#where_async
|
||||
{
|
||||
fn resolve_into_type_async<'b>(
|
||||
&'b self,
|
||||
_info: &'b Self::TypeInfo,
|
||||
type_name: &str,
|
||||
_: Option<&'b [#juniper_crate_name::Selection<'b, #scalar>]>,
|
||||
executor: &'b #juniper_crate_name::Executor<'b, 'b, Self::Context, #scalar>
|
||||
) -> #juniper_crate_name::BoxFuture<'b, #juniper_crate_name::ExecutionResult<#scalar>> {
|
||||
let context = &executor.context();
|
||||
|
||||
#( #resolve_into_type_async )*
|
||||
|
||||
panic!("Concrete type not handled by instance resolvers on {}", #name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let convesion_impls = self.fields.iter().map(|field| {
|
||||
let variant_ty = &field._type;
|
||||
let resolver_code = &field.resolver_code;
|
||||
|
||||
quote!(
|
||||
impl std::convert::From<#variant_ty> for #ty {
|
||||
fn from(val: #variant_ty) -> Self {
|
||||
#resolver_code(val)
|
||||
}
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
let mut type_impl = quote! {
|
||||
#( #convesion_impls )*
|
||||
|
||||
impl #impl_generics #juniper_crate_name::GraphQLType<#scalar> for #ty #where_clause
|
||||
{
|
||||
type Context = #context;
|
||||
type TypeInfo = ();
|
||||
|
||||
fn name(_ : &Self::TypeInfo) -> Option<&str> {
|
||||
Some(#name)
|
||||
}
|
||||
|
||||
fn meta<'r>(
|
||||
info: &Self::TypeInfo,
|
||||
registry: &mut #juniper_crate_name::Registry<'r, #scalar>
|
||||
) -> #juniper_crate_name::meta::MetaType<'r, #scalar>
|
||||
where
|
||||
#scalar: 'r,
|
||||
{
|
||||
let types = &[
|
||||
#( #meta_types )*
|
||||
];
|
||||
registry.build_union_type::<#ty>(
|
||||
info, types
|
||||
)
|
||||
#description
|
||||
.into_meta()
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn concrete_type_name(&self, context: &Self::Context, _info: &Self::TypeInfo) -> String {
|
||||
#concrete_type_resolver
|
||||
}
|
||||
|
||||
fn resolve_into_type(
|
||||
&self,
|
||||
_info: &Self::TypeInfo,
|
||||
type_name: &str,
|
||||
_: Option<&[#juniper_crate_name::Selection<#scalar>]>,
|
||||
executor: &#juniper_crate_name::Executor<Self::Context, #scalar>,
|
||||
) -> #juniper_crate_name::ExecutionResult<#scalar> {
|
||||
let context = &executor.context();
|
||||
|
||||
#( #resolve_into_type )*
|
||||
|
||||
panic!("Concrete type not handled by instance resolvers on {}", #name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if !self.no_async {
|
||||
type_impl.extend(async_type_impl)
|
||||
}
|
||||
|
||||
type_impl
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
Loading…
Reference in a new issue