1
- use proc_macro2:: { Span , TokenStream } ;
1
+ use proc_macro2:: { TokenStream } ;
2
2
use quote:: quote;
3
- use syn:: { self , parse_quote , Data , DeriveInput , Field , Fields , Ident } ;
3
+ use syn:: { self , Data , Fields } ;
4
4
5
- use crate :: util:: * ;
5
+ use crate :: util;
6
6
7
- #[ derive( Default , Debug ) ]
8
- struct ObjAttrs {
9
- name : Option < String > ,
10
- description : Option < String > ,
11
- context : Option < Ident > ,
12
- scalar : Option < Ident > ,
13
- }
14
-
15
- impl ObjAttrs {
16
- fn from_input ( input : & DeriveInput ) -> ObjAttrs {
17
- let mut res = ObjAttrs :: default ( ) ;
18
-
19
- // Check doc comments for description.
20
- res. description = get_doc_comment ( & input. attrs ) ;
21
-
22
- // Check attributes for name and description.
23
- if let Some ( items) = get_graphql_attr ( & input. attrs ) {
24
- for item in items {
25
- if let Some ( AttributeValue :: String ( val) ) =
26
- keyed_item_value ( & item, "name" , AttributeValidation :: String )
27
- {
28
- if is_valid_name ( & * val) {
29
- res. name = Some ( val) ;
30
- continue ;
31
- } else {
32
- panic ! (
33
- "Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but \" {}\" does not" ,
34
- & * val
35
- ) ;
36
- }
37
- }
38
- if let Some ( AttributeValue :: String ( val) ) =
39
- keyed_item_value ( & item, "description" , AttributeValidation :: String )
40
- {
41
- res. description = Some ( val) ;
42
- continue ;
43
- }
44
- if let Some ( AttributeValue :: String ( scalar) ) =
45
- keyed_item_value ( & item, "scalar" , AttributeValidation :: String )
46
- {
47
- res. scalar = Some ( Ident :: new ( & scalar as & str , Span :: call_site ( ) ) ) ;
48
- continue ;
49
- }
50
- if let Some ( AttributeValue :: String ( ctx) ) =
51
- keyed_item_value ( & item, "Context" , AttributeValidation :: String )
52
- {
53
- res. context = Some ( Ident :: new ( & ctx as & str , Span :: call_site ( ) ) ) ;
54
- continue ;
55
- }
56
- panic ! ( format!(
57
- "Unknown struct attribute for #[derive(GraphQLObject)]: {:?}" ,
58
- item
59
- ) ) ;
60
- }
61
- }
62
- res
63
- }
64
- }
65
-
66
- #[ derive( Default ) ]
67
- struct ObjFieldAttrs {
68
- name : Option < String > ,
69
- description : Option < String > ,
70
- deprecation : Option < DeprecationAttr > ,
71
- skip : bool ,
72
- }
73
-
74
- impl ObjFieldAttrs {
75
- fn from_input ( variant : & Field ) -> ObjFieldAttrs {
76
- let mut res = ObjFieldAttrs :: default ( ) ;
77
-
78
- // Check doc comments for description.
79
- res. description = get_doc_comment ( & variant. attrs ) ;
80
-
81
- // Check builtin deprecated attribute for deprecation.
82
- res. deprecation = get_deprecated ( & variant. attrs ) ;
83
7
84
- // Check attributes.
85
- if let Some ( items) = get_graphql_attr ( & variant. attrs ) {
86
- for item in items {
87
- if let Some ( AttributeValue :: String ( val) ) =
88
- keyed_item_value ( & item, "name" , AttributeValidation :: String )
89
- {
90
- if is_valid_name ( & * val) {
91
- res. name = Some ( val) ;
92
- continue ;
93
- } else {
94
- panic ! (
95
- "Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but \" {}\" does not" ,
96
- & * val
97
- ) ;
98
- }
99
- }
100
- if let Some ( AttributeValue :: String ( val) ) =
101
- keyed_item_value ( & item, "description" , AttributeValidation :: String )
102
- {
103
- res. description = Some ( val) ;
104
- continue ;
105
- }
106
- if let Some ( AttributeValue :: String ( val) ) =
107
- keyed_item_value ( & item, "deprecation" , AttributeValidation :: String )
108
- {
109
- res. deprecation = Some ( DeprecationAttr { reason : Some ( val) } ) ;
110
- continue ;
111
- }
112
- match keyed_item_value ( & item, "deprecated" , AttributeValidation :: String ) {
113
- Some ( AttributeValue :: String ( val) ) => {
114
- res. deprecation = Some ( DeprecationAttr { reason : Some ( val) } ) ;
115
- continue ;
116
- }
117
- Some ( AttributeValue :: Bare ) => {
118
- res. deprecation = Some ( DeprecationAttr { reason : None } ) ;
119
- continue ;
120
- }
121
- None => { }
122
- }
123
- if let Some ( _) = keyed_item_value ( & item, "skip" , AttributeValidation :: Bare ) {
124
- res. skip = true ;
125
- continue ;
126
- }
127
- panic ! ( format!(
128
- "Unknown field attribute for #[derive(GraphQLObject)]: {:?}" ,
129
- item
130
- ) ) ;
131
- }
132
- }
133
- res
134
- }
135
- }
136
-
137
- pub fn impl_object ( ast : & syn:: DeriveInput ) -> TokenStream {
138
- let fields = match ast. data {
139
- Data :: Struct ( ref data) => match data. fields {
140
- Fields :: Named ( ref fields) => fields. named . iter ( ) . collect :: < Vec < _ > > ( ) ,
8
+ pub fn build_derive_object ( ast : syn:: DeriveInput ) -> TokenStream {
9
+ let struct_fields = match ast. data {
10
+ Data :: Struct ( data) => match data. fields {
11
+ Fields :: Named ( fields) => fields. named ,
141
12
_ => {
142
13
panic ! ( "#[derive(GraphQLObject)] may only be used on regular structs with fields" ) ;
143
14
}
@@ -148,140 +19,52 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream {
148
19
} ;
149
20
150
21
// Parse attributes.
151
- let ident = & ast. ident ;
152
- let generics = & ast. generics ;
153
- let ident_name = ident. to_string ( ) ;
154
- let attrs = ObjAttrs :: from_input ( ast) ;
155
- let name = attrs. name . unwrap_or ( ast. ident . to_string ( ) ) ;
156
- let build_description = match attrs. description {
157
- Some ( s) => quote ! { builder. description( #s) } ,
158
- None => quote ! { builder } ,
159
- } ;
160
-
161
- let mut meta_fields = TokenStream :: new ( ) ;
162
- let mut resolvers = TokenStream :: new ( ) ;
163
-
164
- for field in fields {
165
- let field_ty = & field. ty ;
166
- let field_attrs = ObjFieldAttrs :: from_input ( field) ;
167
- let field_ident = field. ident . as_ref ( ) . unwrap ( ) ;
168
-
169
- // Check if we should skip this field.
170
- if field_attrs. skip {
171
- continue ;
22
+ let attrs = match util:: ObjectAttributes :: from_attrs ( & ast. attrs ) {
23
+ Ok ( a) => a,
24
+ Err ( e) => {
25
+ panic ! ( "Invalid #[graphql(...)] attribute: {}" , e) ;
172
26
}
27
+ } ;
28
+ let name = attrs. name . unwrap_or ( ast. ident . to_string ( ) ) ;
173
29
174
- // Build value.
175
- let name = match field_attrs. name {
176
- Some ( ref name) => {
177
- // Custom name specified.
178
- name. to_string ( )
179
- }
180
- None => {
181
- // Note: auto camel casing when no custom name specified.
182
- crate :: util:: to_camel_case ( & field_ident. to_string ( ) )
30
+ let fields = struct_fields
31
+ . into_iter ( )
32
+ . filter_map ( |field| {
33
+ let field_attrs = util:: FieldAttributes :: from_attrs ( & field. attrs ) ;
34
+
35
+ if field_attrs. skip {
36
+ None
37
+ } else {
38
+
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
+ ) ;
48
+
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
+ } )
183
57
}
184
- } ;
185
- let build_description = match field_attrs. description {
186
- Some ( s) => quote ! { field. description( #s) } ,
187
- None => quote ! { field } ,
188
- } ;
189
-
190
- let build_deprecation = match field_attrs. deprecation {
191
- Some ( DeprecationAttr { reason : Some ( s) } ) => quote ! { field. deprecated( Some ( #s) ) } ,
192
- Some ( DeprecationAttr { reason : None } ) => quote ! { field. deprecated( None ) } ,
193
- None => quote ! { field } ,
194
- } ;
195
-
196
- meta_fields. extend ( quote ! {
197
- {
198
- let field = registry. field:: <#field_ty>( #name, & ( ) ) ;
199
- let field = #build_description;
200
- let field = #build_deprecation;
201
- field
202
- } ,
203
- } ) ;
204
-
205
- // Build from_input clause.
206
-
207
- resolvers. extend ( quote ! {
208
- #name => executor. resolve_with_ctx( & ( ) , & self . #field_ident) ,
209
58
} ) ;
210
- }
211
-
212
- let ( _, ty_generics, _) = generics. split_for_impl ( ) ;
213
-
214
- let mut generics = generics. clone ( ) ;
215
-
216
- if attrs. scalar . is_none ( ) {
217
- generics. params . push ( parse_quote ! ( __S) ) ;
218
- {
219
- let where_clause = generics. where_clause . get_or_insert ( parse_quote ! ( where ) ) ;
220
- where_clause
221
- . predicates
222
- . push ( parse_quote ! ( __S: juniper:: ScalarValue ) ) ;
223
- where_clause
224
- . predicates
225
- . push ( parse_quote ! ( for <' __b> & ' __b __S: juniper:: ScalarRefValue <' __b>) ) ;
226
- }
227
- }
228
-
229
- let scalar = attrs
230
- . scalar
231
- . unwrap_or_else ( || Ident :: new ( "__S" , Span :: call_site ( ) ) ) ;
232
-
233
- let ctx = attrs
234
- . context
235
- . map ( |ident| quote ! ( #ident ) )
236
- . unwrap_or ( quote ! ( ( ) ) ) ;
237
-
238
- let ( impl_generics, _, where_clause) = generics. split_for_impl ( ) ;
239
59
240
- let body = quote ! {
241
- impl #impl_generics juniper:: GraphQLType <#scalar> for #ident #ty_generics
242
- #where_clause
243
- {
244
- type Context = #ctx;
245
- type TypeInfo = ( ) ;
246
-
247
- fn name( _: & ( ) ) -> Option <& str > {
248
- Some ( #name)
249
- }
250
-
251
- fn concrete_type_name( & self , _: & Self :: Context , _: & ( ) ) -> String {
252
- #name. to_string( )
253
- }
254
-
255
- fn meta<' r>(
256
- _: & ( ) ,
257
- registry: & mut juniper:: Registry <' r, #scalar>
258
- ) -> juniper:: meta:: MetaType <' r, #scalar>
259
- where #scalar: ' r
260
- {
261
- let fields = & [
262
- #( #meta_fields) *
263
- ] ;
264
- let builder = registry. build_object_type:: <#ident>( & ( ) , fields) ;
265
- let builder = #build_description;
266
- builder. into_meta( )
267
- }
268
-
269
- fn resolve_field(
270
- & self ,
271
- _: & ( ) ,
272
- field_name: & str ,
273
- _: & juniper:: Arguments <#scalar>,
274
- executor: & juniper:: Executor <Self :: Context , #scalar>
275
- ) -> juniper:: ExecutionResult <#scalar>
276
- {
277
-
278
- match field_name {
279
- #( #resolvers) *
280
- _ => panic!( "Field {} not found on type {}" , field_name, #ident_name) ,
281
- }
282
-
283
- }
284
- }
60
+ let definition = util:: GraphQLTypeDefiniton {
61
+ name,
62
+ _type : syn:: parse_str ( & ast. ident . to_string ( ) ) . unwrap ( ) ,
63
+ context : attrs. context ,
64
+ scalar : attrs. scalar ,
65
+ description : attrs. description ,
66
+ fields : fields. collect ( ) ,
67
+ generics : ast. generics . clone ( ) ,
285
68
} ;
286
- body
69
+ definition . into_tokens ( )
287
70
}
0 commit comments