Improve union
proc macro
* Rename to graphql_union * Implement full-featured parsing and code generation
This commit is contained in:
parent
6861951a1e
commit
9ce3d04007
12 changed files with 298 additions and 88 deletions
44
integration_tests/juniper_tests/src/codegen/impl_union.rs
Normal file
44
integration_tests/juniper_tests/src/codegen/impl_union.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Trait.
|
||||||
|
|
||||||
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
struct Human {
|
||||||
|
id: String,
|
||||||
|
home_planet: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
struct Droid {
|
||||||
|
id: String,
|
||||||
|
primary_function: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Character {
|
||||||
|
fn as_human(&self) -> Option<&Human> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn as_droid(&self) -> Option<&Droid> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Character for Human {
|
||||||
|
fn as_human(&self) -> Option<&Human> {
|
||||||
|
Some(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Character for Droid {
|
||||||
|
fn as_droid(&self) -> Option<&Droid> {
|
||||||
|
Some(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[juniper::graphql_union]
|
||||||
|
impl<'a> GraphQLUnion for &'a dyn Character {
|
||||||
|
fn resolve(&self) {
|
||||||
|
match self {
|
||||||
|
Human => self.as_human(),
|
||||||
|
Droid => self.as_droid(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,5 +4,5 @@ mod derive_enum;
|
||||||
mod derive_input_object;
|
mod derive_input_object;
|
||||||
mod derive_object;
|
mod derive_object;
|
||||||
mod derive_object_with_raw_idents;
|
mod derive_object_with_raw_idents;
|
||||||
|
mod impl_union;
|
||||||
mod scalar_value_transparent;
|
mod scalar_value_transparent;
|
||||||
mod unions;
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -6,8 +6,12 @@
|
||||||
|
|
||||||
## Breaking Changes
|
## Breaking Changes
|
||||||
|
|
||||||
- Remove ScalarRefValue trait
|
- `graphql_union!` macro removed, replaced by `#[graphql_union]` proc macro
|
||||||
- Change return type of GraphQLType::resolve to `ExecutionResult`
|
|
||||||
|
- ScalarRefValue trait removed
|
||||||
|
Trait was not required.
|
||||||
|
|
||||||
|
- Changed return type of GraphQLType::resolve to `ExecutionResult`
|
||||||
This was done to unify the return type of all resolver methods
|
This was done to unify the return type of all resolver methods
|
||||||
The previous `Value` return type was just an internal artifact of
|
The previous `Value` return type was just an internal artifact of
|
||||||
error handling.
|
error handling.
|
||||||
|
|
|
@ -166,17 +166,8 @@ mod union {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
#[crate::graphql_union_internal]
|
||||||
graphql_union!(<'a> &'a dyn Pet: () as "Pet" |&self| {
|
impl<'a> GraphQLUnion for &'a dyn Pet {
|
||||||
instance_resolvers: |&_| {
|
|
||||||
&Dog => self.as_dog(),
|
|
||||||
&Cat => self.as_cat(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[crate::union_internal]
|
|
||||||
impl<'a> &'a dyn Pet {
|
|
||||||
fn resolve(&self) {
|
fn resolve(&self) {
|
||||||
match self {
|
match self {
|
||||||
Dog => self.as_dog(),
|
Dog => self.as_dog(),
|
||||||
|
|
|
@ -110,13 +110,14 @@ extern crate uuid;
|
||||||
// This allows users to just depend on juniper and get the derive
|
// This allows users to just depend on juniper and get the derive
|
||||||
// functionality automatically.
|
// functionality automatically.
|
||||||
pub use juniper_codegen::{
|
pub use juniper_codegen::{
|
||||||
object, union, GraphQLEnum, GraphQLInputObject, GraphQLObject, GraphQLScalarValue, ScalarValue,
|
graphql_union, object, GraphQLEnum, GraphQLInputObject, GraphQLObject, GraphQLScalarValue,
|
||||||
|
ScalarValue,
|
||||||
};
|
};
|
||||||
// Internal macros are not exported,
|
// Internal macros are not exported,
|
||||||
// but declared at the root to make them easier to use.
|
// but declared at the root to make them easier to use.
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use juniper_codegen::{
|
use juniper_codegen::{
|
||||||
object_internal, union_internal, GraphQLEnumInternal, GraphQLInputObjectInternal,
|
graphql_union_internal, object_internal, GraphQLEnumInternal, GraphQLInputObjectInternal,
|
||||||
GraphQLScalarValueInternal,
|
GraphQLScalarValueInternal,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl Concrete {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::union_internal(name = "ACustomNamedUnion")]
|
#[crate::graphql_union_internal(name = "ACustomNamedUnion")]
|
||||||
impl CustomName {
|
impl CustomName {
|
||||||
fn resolve(&self) {
|
fn resolve(&self) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -56,7 +56,7 @@ impl CustomName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::union_internal]
|
#[crate::graphql_union_internal]
|
||||||
impl<'a> WithLifetime<'a> {
|
impl<'a> WithLifetime<'a> {
|
||||||
fn resolve(&self) {
|
fn resolve(&self) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -67,7 +67,7 @@ impl<'a> WithLifetime<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::union_internal]
|
#[crate::graphql_union_internal]
|
||||||
impl<T> WithGenerics<T> {
|
impl<T> WithGenerics<T> {
|
||||||
fn resolve(&self) {
|
fn resolve(&self) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -78,7 +78,7 @@ impl<T> WithGenerics<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::union_internal(description = "A description")]
|
#[crate::graphql_union_internal(description = "A description")]
|
||||||
impl DescriptionFirst {
|
impl DescriptionFirst {
|
||||||
fn resolve(&self) {
|
fn resolve(&self) {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -234,7 +234,7 @@ impl GraphQLType<DefaultScalarValue> for User
|
||||||
```
|
```
|
||||||
|
|
||||||
*/
|
*/
|
||||||
pub trait GraphQLType<S>: Sized
|
pub trait GraphQLType<S = DefaultScalarValue>: Sized
|
||||||
where
|
where
|
||||||
S: ScalarValue,
|
S: ScalarValue,
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,30 +17,11 @@ struct ResolveBody {
|
||||||
|
|
||||||
impl syn::parse::Parse for ResolveBody {
|
impl syn::parse::Parse for ResolveBody {
|
||||||
fn parse(input: syn::parse::ParseStream) -> Result<Self, syn::parse::Error> {
|
fn parse(input: syn::parse::ParseStream) -> Result<Self, syn::parse::Error> {
|
||||||
input.parse::<syn::token::Fn>()?;
|
input.parse::<syn::token::Match>()?;
|
||||||
let ident = input.parse::<syn::Ident>()?;
|
input.parse::<syn::token::SelfValue>()?;
|
||||||
if ident != "resolve" {
|
|
||||||
return Err(input.error("Expected method named 'resolve'"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let args;
|
|
||||||
syn::parenthesized!(args in input);
|
|
||||||
args.parse::<syn::token::And>()?;
|
|
||||||
args.parse::<syn::token::SelfValue>()?;
|
|
||||||
if !args.is_empty() {
|
|
||||||
return Err(
|
|
||||||
input.error("Unexpected extra tokens: only one '&self' parameter is allowed")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let body;
|
|
||||||
syn::braced!( body in input );
|
|
||||||
|
|
||||||
body.parse::<syn::token::Match>()?;
|
|
||||||
body.parse::<syn::token::SelfValue>()?;
|
|
||||||
|
|
||||||
let match_body;
|
let match_body;
|
||||||
syn::braced!( match_body in body );
|
syn::braced!( match_body in input );
|
||||||
|
|
||||||
let mut variants = Vec::new();
|
let mut variants = Vec::new();
|
||||||
while !match_body.is_empty() {
|
while !match_body.is_empty() {
|
||||||
|
@ -54,7 +35,7 @@ impl syn::parse::Parse for ResolveBody {
|
||||||
match_body.parse::<syn::token::Comma>().ok();
|
match_body.parse::<syn::token::Comma>().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !body.is_empty() {
|
if !input.is_empty() {
|
||||||
return Err(input.error("Unexpected input"));
|
return Err(input.error("Unexpected input"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,36 +48,51 @@ pub fn impl_union(
|
||||||
attrs: TokenStream,
|
attrs: TokenStream,
|
||||||
body: TokenStream,
|
body: TokenStream,
|
||||||
) -> Result<TokenStream, MacroError> {
|
) -> Result<TokenStream, MacroError> {
|
||||||
// We are re-using the object attributes since they are almost the same.
|
let _impl = util::parse_impl::ImplBlock::parse(attrs, body);
|
||||||
let attrs = syn::parse::<util::ObjectAttributes>(attrs)?;
|
|
||||||
|
|
||||||
let item = syn::parse::<syn::ItemImpl>(body)?;
|
// Validate trait target name, if present.
|
||||||
|
if let Some((name, path)) = &_impl.target_trait {
|
||||||
if item.items.len() != 1 {
|
if !(name == "GraphQLUnion" || name == "juniper.GraphQLUnion") {
|
||||||
return Err(MacroError::new(
|
return Err(MacroError::new(
|
||||||
item.span(),
|
path.span(),
|
||||||
|
"Invalid impl target trait: expected 'GraphQLUnion'".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let type_ident = &_impl.type_ident;
|
||||||
|
let name = _impl
|
||||||
|
.attrs
|
||||||
|
.name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| type_ident.to_string());
|
||||||
|
let crate_name = util::juniper_path(is_internal);
|
||||||
|
|
||||||
|
let scalar = _impl
|
||||||
|
.attrs
|
||||||
|
.scalar
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| quote!( #s ))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
quote! { #crate_name::DefaultScalarValue }
|
||||||
|
});
|
||||||
|
|
||||||
|
if _impl.methods.len() != 1 {
|
||||||
|
return Err(MacroError::new(
|
||||||
|
_impl.target_type.span(),
|
||||||
"Invalid impl body: expected one method with signature: fn resolve(&self) { ... }"
|
"Invalid impl body: expected one method with signature: fn resolve(&self) { ... }"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let method = _impl.methods.first().unwrap();
|
||||||
|
|
||||||
let body_item = item.items.first().unwrap();
|
let resolve_args = _impl.parse_resolve_method(method);
|
||||||
let body = quote! { #body_item };
|
|
||||||
let variants = syn::parse::<ResolveBody>(body.into())?.variants;
|
|
||||||
|
|
||||||
let ty = &item.self_ty;
|
let stmts = &method.block.stmts;
|
||||||
|
let body_raw = quote!( #( #stmts )* );
|
||||||
|
let body = syn::parse::<ResolveBody>(body_raw.into())?;
|
||||||
|
|
||||||
let ty_ident = util::name_of_type(&*ty).ok_or_else(|| {
|
let meta_types = body.variants.iter().map(|var| {
|
||||||
MacroError::new(
|
|
||||||
ty.span(),
|
|
||||||
"Expected a path ending in a simple type identifier".to_string(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let name = attrs.name.unwrap_or_else(|| ty_ident.to_string());
|
|
||||||
|
|
||||||
let juniper = util::juniper_path(is_internal);
|
|
||||||
|
|
||||||
let meta_types = variants.iter().map(|var| {
|
|
||||||
let var_ty = &var.ty;
|
let var_ty = &var.ty;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -104,50 +100,45 @@ pub fn impl_union(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let concrete_type_resolver = variants.iter().map(|var| {
|
let concrete_type_resolver = body.variants.iter().map(|var| {
|
||||||
let var_ty = &var.ty;
|
let var_ty = &var.ty;
|
||||||
let resolve = &var.resolver;
|
let resolve = &var.resolver;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
if ({#resolve} as std::option::Option<&#var_ty>).is_some() {
|
if ({#resolve} as std::option::Option<&#var_ty>).is_some() {
|
||||||
return <#var_ty as #juniper::GraphQLType<_>>::name(&()).unwrap().to_string();
|
return <#var_ty as #crate_name::GraphQLType<#scalar>>::name(&()).unwrap().to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let resolve_into_type = variants.iter().map(|var| {
|
let resolve_into_type = body.variants.iter().map(|var| {
|
||||||
let var_ty = &var.ty;
|
let var_ty = &var.ty;
|
||||||
let resolve = &var.resolver;
|
let resolve = &var.resolver;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
if type_name == (<#var_ty as #juniper::GraphQLType<_>>::name(&())).unwrap() {
|
if type_name == (<#var_ty as #crate_name::GraphQLType<#scalar>>::name(&())).unwrap() {
|
||||||
return executor.resolve(&(), &{ #resolve });
|
return executor.resolve(&(), &{ #resolve });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let scalar = attrs
|
let generics = _impl.generics;
|
||||||
.scalar
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| quote!( #s ))
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
quote! { #juniper::DefaultScalarValue }
|
|
||||||
});
|
|
||||||
|
|
||||||
let generics = item.generics.clone();
|
|
||||||
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
let description = match attrs.description.as_ref() {
|
let description = match _impl.description.as_ref() {
|
||||||
Some(value) => quote!( .description( #value ) ),
|
Some(value) => quote!( .description( #value ) ),
|
||||||
None => quote!(),
|
None => quote!(),
|
||||||
};
|
};
|
||||||
let context = attrs
|
let context = _impl
|
||||||
|
.attrs
|
||||||
.context
|
.context
|
||||||
.map(|c| quote! { #c })
|
.map(|c| quote! { #c })
|
||||||
.unwrap_or_else(|| quote! { () });
|
.unwrap_or_else(|| quote! { () });
|
||||||
|
|
||||||
|
let ty = _impl.target_type;
|
||||||
|
|
||||||
let output = quote! {
|
let output = quote! {
|
||||||
impl #impl_generics #juniper::GraphQLType<#scalar> for #ty #where_clause
|
impl #impl_generics #crate_name::GraphQLType<#scalar> for #ty #where_clause
|
||||||
{
|
{
|
||||||
type Context = #context;
|
type Context = #context;
|
||||||
type TypeInfo = ();
|
type TypeInfo = ();
|
||||||
|
@ -158,8 +149,8 @@ pub fn impl_union(
|
||||||
|
|
||||||
fn meta<'r>(
|
fn meta<'r>(
|
||||||
info: &Self::TypeInfo,
|
info: &Self::TypeInfo,
|
||||||
registry: &mut #juniper::Registry<'r, #scalar>
|
registry: &mut #crate_name::Registry<'r, #scalar>
|
||||||
) -> #juniper::meta::MetaType<'r, #scalar>
|
) -> #crate_name::meta::MetaType<'r, #scalar>
|
||||||
where
|
where
|
||||||
#scalar: 'r,
|
#scalar: 'r,
|
||||||
{
|
{
|
||||||
|
@ -184,10 +175,11 @@ pub fn impl_union(
|
||||||
&self,
|
&self,
|
||||||
_info: &Self::TypeInfo,
|
_info: &Self::TypeInfo,
|
||||||
type_name: &str,
|
type_name: &str,
|
||||||
_: Option<&[#juniper::Selection<#scalar>]>,
|
_: Option<&[#crate_name::Selection<#scalar>]>,
|
||||||
executor: &#juniper::Executor<Self::Context, #scalar>,
|
executor: &#crate_name::Executor<Self::Context, #scalar>,
|
||||||
) -> #juniper::ExecutionResult<#scalar> {
|
) -> #crate_name::ExecutionResult<#scalar> {
|
||||||
let context = &executor.context();
|
let context = &executor.context();
|
||||||
|
#( #resolve_args )*
|
||||||
|
|
||||||
#( #resolve_into_type )*
|
#( #resolve_into_type )*
|
||||||
|
|
||||||
|
|
|
@ -389,7 +389,7 @@ pub fn object_internal(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
#[proc_macro_error::proc_macro_error]
|
#[proc_macro_error::proc_macro_error]
|
||||||
pub fn union(attrs: TokenStream, body: TokenStream) -> TokenStream {
|
pub fn graphql_union(attrs: TokenStream, body: TokenStream) -> TokenStream {
|
||||||
let output = match impl_union::impl_union(false, attrs, body) {
|
let output = match impl_union::impl_union(false, attrs, body) {
|
||||||
Ok(toks) => toks,
|
Ok(toks) => toks,
|
||||||
Err(err) => proc_macro_error::abort!(err),
|
Err(err) => proc_macro_error::abort!(err),
|
||||||
|
@ -400,7 +400,7 @@ pub fn union(attrs: TokenStream, body: TokenStream) -> TokenStream {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
#[proc_macro_error::proc_macro_error]
|
#[proc_macro_error::proc_macro_error]
|
||||||
pub fn union_internal(attrs: TokenStream, body: TokenStream) -> TokenStream {
|
pub fn graphql_union_internal(attrs: TokenStream, body: TokenStream) -> TokenStream {
|
||||||
let output = match impl_union::impl_union(true, attrs, body) {
|
let output = match impl_union::impl_union(true, attrs, body) {
|
||||||
Ok(toks) => toks,
|
Ok(toks) => toks,
|
||||||
Err(err) => proc_macro_error::abort!(err),
|
Err(err) => proc_macro_error::abort!(err),
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod parse_impl;
|
||||||
|
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use syn::{
|
use syn::{
|
177
juniper_codegen/src/util/parse_impl.rs
Normal file
177
juniper_codegen/src/util/parse_impl.rs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
//! Parse impl blocks.
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
|
pub struct ImplBlock {
|
||||||
|
pub attrs: util::ObjectAttributes,
|
||||||
|
pub target_trait: Option<(String, syn::Path)>,
|
||||||
|
pub target_type: Box<syn::Type>,
|
||||||
|
pub type_ident: syn::Ident,
|
||||||
|
pub generics: syn::Generics,
|
||||||
|
// _impl: syn::ItemImpl,
|
||||||
|
pub methods: Vec<syn::ImplItemMethod>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImplBlock {
|
||||||
|
/// Parse a 'fn resolve()' metho declaration found in union or interface
|
||||||
|
/// impl blocks.
|
||||||
|
/// Returns the variable definitions needed for the resolve body.
|
||||||
|
pub fn parse_resolve_method(
|
||||||
|
&self,
|
||||||
|
method: &syn::ImplItemMethod,
|
||||||
|
) -> Vec<proc_macro2::TokenStream> {
|
||||||
|
if method.sig.ident != "resolve" {
|
||||||
|
panic!("Expect a method named 'fn resolve(...)");
|
||||||
|
}
|
||||||
|
|
||||||
|
let _type = match &method.sig.output {
|
||||||
|
syn::ReturnType::Type(_, _) => {
|
||||||
|
panic!("resolve() method must not have a declared return type");
|
||||||
|
}
|
||||||
|
syn::ReturnType::Default => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut arguments = method.sig.inputs.iter();
|
||||||
|
|
||||||
|
// Verify '&self' argument.
|
||||||
|
match arguments.next() {
|
||||||
|
Some(syn::FnArg::Receiver(rec)) => {
|
||||||
|
if rec.reference.is_none() || rec.mutability.is_some() {
|
||||||
|
panic!(
|
||||||
|
"Invalid method receiver {}(self, ...): did you mean '&self'?",
|
||||||
|
method.sig.ident
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Expected a '&self' argument");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut resolve_parts = Vec::new();
|
||||||
|
|
||||||
|
for arg in arguments {
|
||||||
|
match arg {
|
||||||
|
syn::FnArg::Receiver(_) => {
|
||||||
|
panic!(
|
||||||
|
"Malformed method signature {}: self receiver must be the first argument",
|
||||||
|
method.sig.ident
|
||||||
|
);
|
||||||
|
}
|
||||||
|
syn::FnArg::Typed(captured) => {
|
||||||
|
let (arg_ident, _is_mut) = match &*captured.pat {
|
||||||
|
syn::Pat::Ident(ref pat_ident) => {
|
||||||
|
(&pat_ident.ident, pat_ident.mutability.is_some())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Invalid token for function argument");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let context_type = self.attrs.context.as_ref();
|
||||||
|
|
||||||
|
// Check for executor arguments.
|
||||||
|
if util::type_is_identifier_ref(&captured.ty, "Executor") {
|
||||||
|
resolve_parts.push(quote!(let #arg_ident = executor;));
|
||||||
|
}
|
||||||
|
// Make sure executor is specified as a reference.
|
||||||
|
else if util::type_is_identifier(&captured.ty, "Executor") {
|
||||||
|
panic!("Invalid executor argument: to access the Executor, you need to specify the type as a reference.\nDid you mean &Executor?");
|
||||||
|
}
|
||||||
|
// Check for context arg.
|
||||||
|
else if context_type
|
||||||
|
.clone()
|
||||||
|
.map(|ctx| util::type_is_ref_of(&captured.ty, ctx))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
resolve_parts.push(quote!( let #arg_ident = executor.context(); ));
|
||||||
|
}
|
||||||
|
// Make sure the user does not specify the Context
|
||||||
|
// without a reference. (&Context)
|
||||||
|
else if context_type
|
||||||
|
.clone()
|
||||||
|
.map(|ctx| ctx == &*captured.ty)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
panic!(
|
||||||
|
"Invalid context argument: to access the context, you need to specify the type as a reference.\nDid you mean &{}?",
|
||||||
|
quote!(captured.ty),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("Invalid argument for 'resolve' method: only executor or context are allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_parts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(attr_tokens: TokenStream, body: TokenStream) -> ImplBlock {
|
||||||
|
let attrs = match syn::parse::<util::ObjectAttributes>(attr_tokens) {
|
||||||
|
Ok(attrs) => attrs,
|
||||||
|
Err(e) => {
|
||||||
|
panic!("Invalid attributes:\n{}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut _impl = match syn::parse::<syn::ItemImpl>(body) {
|
||||||
|
Ok(item) => item,
|
||||||
|
Err(err) => {
|
||||||
|
panic!("Parsing error:\n{}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let target_trait = match _impl.trait_ {
|
||||||
|
Some((_, path, _)) => {
|
||||||
|
let name = path
|
||||||
|
.segments
|
||||||
|
.iter()
|
||||||
|
.map(|segment| segment.ident.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(".");
|
||||||
|
Some((name, path))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_ident = if let Some(ident) = util::name_of_type(&*_impl.self_ty) {
|
||||||
|
ident
|
||||||
|
} else {
|
||||||
|
panic!("Could not determine a name for the impl type");
|
||||||
|
};
|
||||||
|
|
||||||
|
let target_type = _impl.self_ty;
|
||||||
|
|
||||||
|
let description = attrs
|
||||||
|
.description
|
||||||
|
.clone()
|
||||||
|
.or(util::get_doc_comment(&_impl.attrs));
|
||||||
|
|
||||||
|
let mut methods = Vec::new();
|
||||||
|
|
||||||
|
for item in _impl.items {
|
||||||
|
match item {
|
||||||
|
syn::ImplItem::Method(method) => {
|
||||||
|
methods.push(method);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Invalid item for GraphQL Object: only type declarations and methods are allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
attrs,
|
||||||
|
type_ident,
|
||||||
|
target_trait,
|
||||||
|
target_type,
|
||||||
|
generics: _impl.generics,
|
||||||
|
description,
|
||||||
|
methods,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue