1+ use darling:: usage:: { GenericsExt , Purpose , UsesTypeParams } ;
2+ use syn:: { parse_quote, Token , WhereClause } ;
3+
14use {
25 proc_macro2:: { Span , TokenStream } ,
36 quote:: { quote, quote_spanned} ,
@@ -28,6 +31,7 @@ pub struct FragmentImpl<'schema, 'a> {
2831 variables_fields : syn:: Type ,
2932 graphql_type_name : String ,
3033 schema_type_path : syn:: Path ,
34+ additional_where : Option < syn:: WhereClause > ,
3135}
3236
3337#[ allow( clippy:: large_enum_variant) ]
@@ -83,6 +87,14 @@ impl<'schema, 'a: 'schema> FragmentImpl<'schema, 'a> {
8387 let variables_fields = variables_fields_path ( variables) ;
8488 let variables_fields = variables_fields. as_ref ( ) ;
8589
90+ let additional_where = additional_where_clause (
91+ generics,
92+ fields,
93+ schema,
94+ & field_module_path,
95+ variables_fields,
96+ ) ;
97+
8698 let selections = fields
8799 . iter ( )
88100 . map ( |( field, schema_field) | {
@@ -111,6 +123,7 @@ impl<'schema, 'a: 'schema> FragmentImpl<'schema, 'a> {
111123 variables_fields,
112124 graphql_type_name : graphql_type_name. to_string ( ) ,
113125 schema_type_path,
126+ additional_where,
114127 } )
115128 }
116129}
@@ -167,6 +180,69 @@ fn process_field<'a>(
167180 } ) )
168181}
169182
183+ fn additional_where_clause (
184+ generics : & syn:: Generics ,
185+ fields : & [ ( FragmentDeriveField , Option < Field < ' _ > > ) ] ,
186+ schema : & Schema < ' _ , Unvalidated > ,
187+ field_module_path : & syn:: Path ,
188+ variables_fields : Option < & syn:: Path > ,
189+ ) -> Option < WhereClause > {
190+ let all_params = generics. declared_type_params ( ) ;
191+ if all_params. is_empty ( ) {
192+ return None ;
193+ }
194+ let options = Purpose :: BoundImpl . into ( ) ;
195+
196+ let mut predicates: Vec < syn:: WherePredicate > = vec ! [ ] ;
197+
198+ for ( field, schema_field) in fields {
199+ let Some ( schema_field) = schema_field else {
200+ continue ;
201+ } ;
202+ let inner_type = schema_field. field_type . inner_type ( schema) ;
203+ if !inner_type. is_composite ( ) {
204+ // We only care about generics on composite types for now
205+ continue ;
206+ }
207+ if * field. spread || * field. flatten {
208+ // We could probably support these, but I don't want to figure it out right now.
209+ // Leave it to the user to provide these bounds
210+ continue ;
211+ }
212+ if field. ty . uses_type_params ( & options, & all_params) . is_empty ( ) {
213+ // If this field uses no type params we skip it
214+ continue ;
215+ }
216+
217+ let ty = & field. ty ;
218+ let marker_ty = schema_field. marker_ident ( ) . to_path ( field_module_path) ;
219+ predicates. push ( parse_quote ! {
220+ #ty: cynic:: QueryFragment <SchemaType = <#marker_ty as cynic:: schema:: Field >:: Type >
221+ } ) ;
222+ match variables_fields {
223+ Some ( variables_fields) => {
224+ predicates. push ( parse_quote ! {
225+ #variables_fields: cynic:: queries:: VariableMatch <<#ty as cynic:: QueryFragment >:: VariablesFields >
226+ } ) ;
227+ }
228+ None => {
229+ predicates. push ( parse_quote ! {
230+ ( ) : cynic:: queries:: VariableMatch <<#ty as cynic:: QueryFragment >:: VariablesFields >
231+ } ) ;
232+ }
233+ }
234+ }
235+
236+ if predicates. is_empty ( ) {
237+ return None ;
238+ }
239+
240+ Some ( WhereClause {
241+ where_token : <Token ! [ where ] >:: default ( ) ,
242+ predicates : predicates. into_iter ( ) . collect ( ) ,
243+ } )
244+ }
245+
170246impl quote:: ToTokens for FragmentImpl < ' _ , ' _ > {
171247 fn to_tokens ( & self , tokens : & mut TokenStream ) {
172248 use quote:: TokenStreamExt ;
@@ -179,6 +255,17 @@ impl quote::ToTokens for FragmentImpl<'_, '_> {
179255 let fragment_name = proc_macro2:: Literal :: string ( & target_struct. to_string ( ) ) ;
180256 let ( impl_generics, ty_generics, where_clause) = self . generics . split_for_impl ( ) ;
181257
258+ let where_clause = match ( where_clause, & self . additional_where ) {
259+ ( None , None ) => None ,
260+ ( Some ( lhs) , None ) => Some ( quote ! { #lhs } ) ,
261+ ( None , Some ( rhs) ) => Some ( quote ! { #rhs } ) ,
262+ ( Some ( lhs) , Some ( rhs) ) => {
263+ let mut new = lhs. clone ( ) ;
264+ new. predicates . extend ( rhs. predicates . clone ( ) ) ;
265+ Some ( quote ! { #new } )
266+ }
267+ } ;
268+
182269 tokens. append_all ( quote ! {
183270 #[ automatically_derived]
184271 impl #impl_generics cynic:: QueryFragment for #target_struct #ty_generics #where_clause {
@@ -383,6 +470,13 @@ impl quote::ToTokens for SpreadSelection {
383470}
384471
385472impl OutputType < ' _ > {
473+ fn is_composite ( & self ) -> bool {
474+ matches ! (
475+ self ,
476+ OutputType :: Object ( _) | OutputType :: Interface ( _) | OutputType :: Union ( _)
477+ )
478+ }
479+
386480 fn as_kind ( & self ) -> FieldKind {
387481 match self {
388482 OutputType :: Scalar ( _) => FieldKind :: Scalar ,
0 commit comments