Skip to content

Commit 4fe2e50

Browse files
committed
(codegen) Tweak impl_object implementation.
1 parent 1790bc0 commit 4fe2e50

File tree

4 files changed

+162
-130
lines changed

4 files changed

+162
-130
lines changed

juniper_codegen/src/derive_object.rs

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use proc_macro2::{TokenStream};
1+
use proc_macro2::TokenStream;
22
use quote::quote;
33
use syn::{self, Data, Fields};
44

55
use crate::util;
66

7-
8-
pub fn build_derive_object(ast: syn::DeriveInput) -> TokenStream {
7+
pub fn build_derive_object(ast: syn::DeriveInput, is_internal: bool) -> TokenStream {
98
let struct_fields = match ast.data {
109
Data::Struct(data) => match data.fields {
1110
Fields::Named(fields) => fields.named,
@@ -27,35 +26,32 @@ pub fn build_derive_object(ast: syn::DeriveInput) -> TokenStream {
2726
};
2827
let name = attrs.name.unwrap_or(ast.ident.to_string());
2928

30-
let fields = struct_fields
31-
.into_iter()
32-
.filter_map(|field| {
33-
let field_attrs = util::FieldAttributes::from_attrs(&field.attrs);
29+
let fields = struct_fields.into_iter().filter_map(|field| {
30+
let field_attrs = util::FieldAttributes::from_attrs(&field.attrs);
3431

35-
if field_attrs.skip {
36-
None
37-
} else {
32+
if field_attrs.skip {
33+
None
34+
} else {
35+
let field_name = field.ident.unwrap();
36+
let name = field_attrs
37+
.name
38+
.clone()
39+
.unwrap_or_else(|| util::to_camel_case(&field_name.to_string()));
3840

39-
let field_name = field.ident.unwrap();
40-
let name = field_attrs
41-
.name
42-
.clone()
43-
.unwrap_or_else(|| util::to_camel_case(&field_name.to_string()));
44-
45-
let resolver_code = quote!(
46-
&self . #field_name
47-
);
41+
let resolver_code = quote!(
42+
&self . #field_name
43+
);
4844

49-
Some(util::GraphQLTypeDefinitionField{
50-
name,
51-
_type: field.ty,
52-
args: Vec::new(),
53-
description: field_attrs.description,
54-
deprecation: field_attrs.deprecation,
55-
resolver_code,
56-
})
57-
}
58-
});
45+
Some(util::GraphQLTypeDefinitionField {
46+
name,
47+
_type: field.ty,
48+
args: Vec::new(),
49+
description: field_attrs.description,
50+
deprecation: field_attrs.deprecation,
51+
resolver_code,
52+
})
53+
}
54+
});
5955

6056
let definition = util::GraphQLTypeDefiniton {
6157
name,
@@ -66,5 +62,7 @@ pub fn build_derive_object(ast: syn::DeriveInput) -> TokenStream {
6662
fields: fields.collect(),
6763
generics: ast.generics.clone(),
6864
};
69-
definition.into_tokens()
65+
66+
let juniper_crate_name = if is_internal { "crate" } else { "juniper" };
67+
definition.into_tokens(juniper_crate_name)
7068
}

juniper_codegen/src/impl_object.rs

Lines changed: 64 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use proc_macro::TokenStream;
33
use quote::quote;
44

55
/// Generate code for the juniper::impl_object macro.
6-
pub fn build_impl_object(args: TokenStream, body: TokenStream) -> TokenStream {
6+
pub fn build_impl_object(args: TokenStream, body: TokenStream, is_internal: bool) -> TokenStream {
77
let impl_attrs = match syn::parse::<util::ObjectAttributes>(args) {
88
Ok(attrs) => attrs,
99
Err(e) => {
@@ -27,29 +27,44 @@ pub fn build_impl_object(args: TokenStream, body: TokenStream) -> TokenStream {
2727
.map(|segment| segment.ident.to_string())
2828
.collect::<Vec<_>>()
2929
.join(".");
30-
if ! (name == "GraphQLObject" || name == "juniper.GraphQLObject") {
30+
if !(name == "GraphQLObject" || name == "juniper.GraphQLObject") {
3131
panic!("The impl block must implement the 'GraphQLObject' trait");
3232
}
3333
}
3434
None => {
35-
panic!("The impl block must implement the 'GraphQLObject' trait");
35+
// panic!("The impl block must implement the 'GraphQLObject' trait");
3636
}
3737
}
3838

39-
let type_name = match &*_impl.self_ty {
40-
syn::Type::Path(ref type_path) => type_path
41-
.path
42-
.segments
43-
.iter()
44-
.last()
45-
.unwrap()
46-
.ident
47-
.to_string(),
48-
_ => {
49-
panic!("Invalid impl target: expected a path");
50-
}
39+
let name = match impl_attrs.name.as_ref() {
40+
Some(type_name) => type_name.clone(),
41+
None => match &*_impl.self_ty {
42+
syn::Type::Path(ref type_path) => type_path
43+
.path
44+
.segments
45+
.iter()
46+
.last()
47+
.unwrap()
48+
.ident
49+
.to_string(),
50+
syn::Type::Reference(ref reference) => match &*reference.elem {
51+
syn::Type::Path(ref type_path) => type_path
52+
.path
53+
.segments
54+
.iter()
55+
.last()
56+
.unwrap()
57+
.ident
58+
.to_string(),
59+
_ => {
60+
panic!("Could not determine a name for the object type: specify one with #[juniper::impl_object(name = \"SomeName\")");
61+
}
62+
},
63+
_ => {
64+
panic!("Could not determine a name for the object type: specify one with #[juniper::impl_object(name = \"SomeName\")");
65+
}
66+
},
5167
};
52-
let name = impl_attrs.name.unwrap_or(type_name);
5368

5469
let target_type = *_impl.self_ty.clone();
5570

@@ -65,15 +80,15 @@ pub fn build_impl_object(args: TokenStream, body: TokenStream) -> TokenStream {
6580

6681
for item in &mut _impl.items {
6782
match item {
68-
syn::ImplItem::Type(_ty) => {
69-
if _ty.ident == "Context" {
70-
definition.context = Some(_ty.ty.clone());
71-
} else if _ty.ident == "Scalar" {
72-
definition.scalar = Some(_ty.ty.clone());
73-
} else {
74-
panic!("Invalid 'type {} = _' specification. only 'Context' and 'Scalar' are allowed.");
75-
}
76-
}
83+
// syn::ImplItem::Type(_ty) => {
84+
// if _ty.ident == "Context" {
85+
// definition.context = Some(_ty.ty.clone());
86+
// } else if _ty.ident == "Scalar" {
87+
// definition.scalar = Some(_ty.ty.clone());
88+
// } else {
89+
// panic!("Invalid 'type {} = _' specification. only 'Context' and 'Scalar' are allowed.");
90+
// }
91+
// }
7792
syn::ImplItem::Method(method) => {
7893
let _type = match &method.sig.decl.output {
7994
syn::ReturnType::Type(_, ref t) => (**t).clone(),
@@ -87,49 +102,52 @@ pub fn build_impl_object(args: TokenStream, body: TokenStream) -> TokenStream {
87102

88103
let attrs = util::FieldAttributes::from_attrs(&method.attrs);
89104
// Remove graphql attribute from final output.
90-
method
91-
.attrs
92-
.retain(|item| util::path_eq_single(&item.path, "graphql") == false);
105+
// method
106+
// .attrs
107+
// .retain(|item| util::path_eq_single(&item.path, "graphql") == false);
93108

94109
let mut args = Vec::new();
95-
let mut resolve_args = Vec::new();
110+
let mut resolve_parts = Vec::new();
96111

97-
for arg in &method.sig.decl.inputs {
112+
for arg in &mut method.sig.decl.inputs {
98113
match arg {
99-
syn::FnArg::SelfRef(_) => {
114+
_self @ syn::FnArg::SelfRef(_) => {
100115
// Can be ignored.
101-
resolve_args.push(quote!(self));
116+
// "self" will already be in scope.
117+
// resolve_args.push(quote!(self));
102118
}
103119
syn::FnArg::SelfValue(_) => {
104120
panic!(
105-
"Invalid method receiver in {}: self is not allowed, did you mean '&self'?",
121+
"Invalid method receiver {}(self, ...): did you mean '&self'?",
106122
method.sig.ident
107123
);
108124
}
109125
syn::FnArg::Captured(ref captured) => {
110-
let arg_name = match &captured.pat {
111-
syn::Pat::Ident(ref pat_ident) => pat_ident.ident.to_string(),
126+
let arg_ident = match &captured.pat {
127+
syn::Pat::Ident(ref pat_ident) => &pat_ident.ident,
112128
_ => {
113129
panic!("Invalid token for function argument");
114130
}
115131
};
132+
let arg_name = arg_ident.to_string();
116133

117134
// Check if the argument refers to the Executor or the context.
118135
if util::type_is_identifier_ref(&captured.ty, "Executor") {
119-
resolve_args.push(quote!(executor));
136+
resolve_parts.push(quote!(let #arg_ident = executor;));
120137
} else if definition
121138
.context
122139
.as_ref()
123140
.map(|ctx| util::type_is_ref_of(&captured.ty, ctx))
124141
.unwrap_or(false)
125142
{
126-
resolve_args.push(quote!(executor.context()));
143+
resolve_parts.push(quote!( let #arg_ident = executor.context(); ));
127144
} else {
145+
let arg_ident = &captured.pat;
128146
let ty = &captured.ty;
129-
resolve_args.push(quote!(
130-
args
147+
resolve_parts.push(quote!(
148+
let #arg_ident = args
131149
.get::<#ty>(#arg_name)
132-
.expect("Argument missing - validation must have failed")
150+
.expect("Argument missing - validation must have failed");
133151
));
134152
args.push(util::GraphQLTypeDefinitionFieldArg {
135153
name: arg_name,
@@ -142,14 +160,15 @@ pub fn build_impl_object(args: TokenStream, body: TokenStream) -> TokenStream {
142160
}
143161
}
144162

145-
let method_ident = &method.sig.ident;
163+
let body = &method.block;
146164
let resolver_code = quote!(
147-
#target_type :: #method_ident ( #( #resolve_args ),* )
165+
#( #resolve_parts )*
166+
#body
148167
);
149168

150169
let name = attrs
151170
.name
152-
.unwrap_or(util::to_camel_case(&method_ident.to_string()));
171+
.unwrap_or(util::to_camel_case(&method.sig.ident.to_string()));
153172

154173
definition.fields.push(util::GraphQLTypeDefinitionField {
155174
name,
@@ -165,11 +184,6 @@ pub fn build_impl_object(args: TokenStream, body: TokenStream) -> TokenStream {
165184
}
166185
}
167186
}
168-
169-
let graphql_type_impl = definition.into_tokens();
170-
let body = quote!(
171-
#_impl
172-
#graphql_type_impl
173-
);
174-
body.into()
187+
let juniper_crate_name = if is_internal { "crate" } else { "juniper" };
188+
definition.into_tokens(juniper_crate_name).into()
175189
}

juniper_codegen/src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
77
#![recursion_limit = "1024"]
88

9+
extern crate self as juniper;
10+
911
extern crate proc_macro;
1012

1113
mod derive_enum;
@@ -50,7 +52,7 @@ pub fn derive_input_object_internal(input: TokenStream) -> TokenStream {
5052
#[proc_macro_derive(GraphQLObject, attributes(graphql))]
5153
pub fn derive_object(input: TokenStream) -> TokenStream {
5254
let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
53-
let gen = derive_object::build_derive_object(ast);
55+
let gen = derive_object::build_derive_object(ast, false);
5456
gen.into()
5557
}
5658

@@ -78,6 +80,14 @@ pub fn derive_scalar_value_internal(input: TokenStream) -> TokenStream {
7880
/// A proc macro for defining a GraphQL object.
7981
#[proc_macro_attribute]
8082
pub fn impl_object(args: TokenStream, input: TokenStream) -> TokenStream {
81-
let gen = impl_object::build_impl_object(args, input);
83+
let gen = impl_object::build_impl_object(args, input, false);
84+
gen.into()
85+
}
86+
87+
/// A proc macro for defining a GraphQL object.
88+
#[doc(hidden)]
89+
#[proc_macro_attribute]
90+
pub fn impl_object_internal(args: TokenStream, input: TokenStream) -> TokenStream {
91+
let gen = impl_object::build_impl_object(args, input, true);
8292
gen.into()
8393
}

0 commit comments

Comments
 (0)