@@ -132,18 +132,33 @@ fn input_lifetime(generics: &syn::Generics) -> Option<&syn::Lifetime> {
132
132
lifetime
133
133
}
134
134
135
+ struct Generics {
136
+ ty_generic_params : syn:: punctuated:: Punctuated < syn:: GenericParam , syn:: token:: Comma > ,
137
+
138
+ edited_ty_generic_params : syn:: punctuated:: Punctuated < syn:: GenericParam , syn:: token:: Comma > ,
139
+ edited_ty_where_clause : syn:: WhereClause ,
140
+
141
+ impl_diffable_generic_params : syn:: punctuated:: Punctuated < syn:: GenericParam , syn:: token:: Comma > ,
142
+ impl_diffable_where_clause : syn:: WhereClause ,
143
+
144
+ impl_lifetime : syn:: Lifetime ,
145
+ }
146
+
135
147
#[ proc_macro_derive( Diffus ) ]
136
148
pub fn derive_diffus ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
137
149
let input: syn:: DeriveInput = syn:: parse2 ( proc_macro2:: TokenStream :: from ( input) ) . unwrap ( ) ;
138
150
139
151
let ident = & input. ident ;
140
152
let vis = & input. vis ;
141
- let where_clause = & input . generics . where_clause ;
153
+
142
154
let edited_ident = syn:: parse_str :: < syn:: Path > ( & format ! ( "Edited{}" , ident) ) . unwrap ( ) ;
143
155
144
- let data_lifetime = input_lifetime ( & input. generics ) ;
145
- let default_lifetime = syn:: parse_str :: < syn:: Lifetime > ( "'diffus_a" ) . unwrap ( ) ;
146
- let impl_lifetime = data_lifetime. unwrap_or ( & default_lifetime) ;
156
+ let Generics {
157
+ ty_generic_params,
158
+ edited_ty_generic_params, edited_ty_where_clause,
159
+ impl_diffable_generic_params, impl_diffable_where_clause,
160
+ impl_lifetime,
161
+ } = Generics :: new ( & input. generics , & input. data ) ;
147
162
148
163
#[ cfg( feature = "serialize-impl" ) ]
149
164
let derive_serialize = Some ( quote ! { #[ derive( serde:: Serialize ) ] } ) ;
@@ -182,8 +197,8 @@ pub fn derive_diffus(input: proc_macro::TokenStream) -> proc_macro::TokenStream
182
197
}
183
198
} ) ;
184
199
185
- let unit_enum_impl_lifetime = if has_non_unit_variant {
186
- Some ( impl_lifetime . clone ( ) )
200
+ let unit_enum_generic_params = if has_non_unit_variant {
201
+ Some ( edited_ty_generic_params . clone ( ) )
187
202
} else {
188
203
None
189
204
} ;
@@ -262,12 +277,12 @@ pub fn derive_diffus(input: proc_macro::TokenStream) -> proc_macro::TokenStream
262
277
263
278
quote ! {
264
279
#derive_serialize
265
- #vis enum #edited_ident <#unit_enum_impl_lifetime> where #where_clause {
280
+ #vis enum #edited_ident <#unit_enum_generic_params> #edited_ty_where_clause {
266
281
#( #edit_variants) , *
267
282
}
268
283
269
- impl <#impl_lifetime > diffus:: Diffable <#impl_lifetime> for #ident <#data_lifetime> where #where_clause {
270
- type Diff = diffus:: edit:: enm:: Edit <#impl_lifetime, Self , #edited_ident <#unit_enum_impl_lifetime >>;
284
+ impl <#impl_diffable_generic_params > diffus:: Diffable <#impl_lifetime> for #ident <#ty_generic_params> #impl_diffable_where_clause {
285
+ type Diff = diffus:: edit:: enm:: Edit <#impl_lifetime, Self , #edited_ident <#unit_enum_generic_params >>;
271
286
272
287
fn diff( & #impl_lifetime self , other: & #impl_lifetime Self ) -> diffus:: edit:: Edit <#impl_lifetime, Self > {
273
288
match ( self , other) {
@@ -290,12 +305,12 @@ pub fn derive_diffus(input: proc_macro::TokenStream) -> proc_macro::TokenStream
290
305
syn:: Fields :: Named ( _) => {
291
306
quote ! {
292
307
#derive_serialize
293
- #vis struct #edited_ident<#impl_lifetime> where #where_clause {
308
+ #vis struct #edited_ident<#edited_ty_generic_params> #edited_ty_where_clause {
294
309
#edit_fields
295
310
}
296
311
297
- impl <#impl_lifetime > diffus:: Diffable <#impl_lifetime> for #ident <#data_lifetime> where #where_clause {
298
- type Diff = #edited_ident<#impl_lifetime >;
312
+ impl <#impl_diffable_generic_params > diffus:: Diffable <#impl_lifetime> for #ident <#ty_generic_params> #impl_diffable_where_clause {
313
+ type Diff = #edited_ident<#edited_ty_generic_params >;
299
314
300
315
fn diff( & #impl_lifetime self , other: & #impl_lifetime Self ) -> diffus:: edit:: Edit <#impl_lifetime, Self > {
301
316
match ( #field_diffs ) {
@@ -311,10 +326,10 @@ pub fn derive_diffus(input: proc_macro::TokenStream) -> proc_macro::TokenStream
311
326
syn:: Fields :: Unnamed ( _) => {
312
327
quote ! {
313
328
#derive_serialize
314
- #vis struct #edited_ident<#impl_lifetime > ( #edit_fields ) where #where_clause ;
329
+ #vis struct #edited_ident<#edited_ty_generic_params > ( #edit_fields ) #edited_ty_where_clause ;
315
330
316
- impl <#impl_lifetime > diffus:: Diffable <#impl_lifetime> for #ident <#data_lifetime> where #where_clause {
317
- type Diff = #edited_ident<#impl_lifetime >;
331
+ impl <#impl_diffable_generic_params > diffus:: Diffable <#impl_lifetime> for #ident <#ty_generic_params> #impl_diffable_where_clause {
332
+ type Diff = #edited_ident<#edited_ty_generic_params >;
318
333
319
334
fn diff( & #impl_lifetime self , other: & #impl_lifetime Self ) -> diffus:: edit:: Edit <#impl_lifetime, Self > {
320
335
match ( #field_diffs ) {
@@ -330,9 +345,9 @@ pub fn derive_diffus(input: proc_macro::TokenStream) -> proc_macro::TokenStream
330
345
syn:: Fields :: Unit => {
331
346
quote ! {
332
347
#derive_serialize
333
- #vis struct #edited_ident< > where #where_clause ;
348
+ #vis struct #edited_ident< > #edited_ty_where_clause ;
334
349
335
- impl <#impl_lifetime> diffus:: Diffable <#impl_lifetime> for #ident< > where #where_clause {
350
+ impl <#impl_lifetime> diffus:: Diffable <#impl_lifetime> for #ident< > #impl_diffable_where_clause {
336
351
type Diff = #edited_ident;
337
352
338
353
fn diff( & #impl_lifetime self , other: & #impl_lifetime Self ) -> diffus:: edit:: Edit <#impl_lifetime, Self > {
@@ -346,3 +361,112 @@ pub fn derive_diffus(input: proc_macro::TokenStream) -> proc_macro::TokenStream
346
361
syn:: Data :: Union ( _) => panic ! ( "union type not supported yet" ) ,
347
362
} )
348
363
}
364
+
365
+ impl Generics {
366
+ pub fn new (
367
+ input_generics : & syn:: Generics ,
368
+ data : & syn:: Data ,
369
+ ) -> Self {
370
+ let input_generic_params = & input_generics. params ;
371
+ let input_where_clause = & input_generics. where_clause ;
372
+ let empty_where_clause = input_generics. clone ( ) . make_where_clause ( ) . clone ( ) ;
373
+
374
+ let generic_types_used = Self :: collect_generic_types_used ( input_generics, data) ;
375
+
376
+ let ty_generic_params = input_generic_params. clone ( ) ;
377
+
378
+ let mut edited_ty_generic_params = ty_generic_params. clone ( ) ;
379
+ let mut edited_ty_where_clause = input_where_clause. clone ( ) . unwrap_or ( empty_where_clause. clone ( ) ) ;
380
+
381
+ let mut impl_diffable_generic_params = input_generic_params. clone ( ) ;
382
+ let mut impl_diffable_where_clause = input_where_clause. clone ( ) . unwrap_or ( empty_where_clause) ;
383
+
384
+ let explicit_data_lifetime = input_lifetime ( input_generics) ;
385
+ let impl_lifetime = explicit_data_lifetime. cloned ( ) . unwrap_or_else ( || {
386
+ let default_lifetime = syn:: parse_str :: < syn:: Lifetime > ( "'diffus_a" ) . unwrap ( ) ;
387
+
388
+ // Add the lifetime into the generics lists.
389
+ impl_diffable_generic_params. insert ( 0 , syn:: GenericParam :: Lifetime ( syn:: LifetimeDef :: new ( default_lifetime. clone ( ) ) ) ) ;
390
+ edited_ty_generic_params. insert ( 0 , syn:: GenericParam :: Lifetime ( syn:: LifetimeDef :: new ( default_lifetime. clone ( ) ) ) ) ;
391
+
392
+ default_lifetime. clone ( )
393
+ } ) ;
394
+
395
+ // Ensure that all generic types that exist live for as long as the diffus lifetime.
396
+ impl_diffable_where_clause. predicates . extend ( input_generics. type_params ( ) . map ( |type_param| {
397
+ let where_predicate = quote ! ( #type_param : #impl_lifetime) ;
398
+ let where_predicate: syn:: WherePredicate = syn:: parse ( where_predicate. into ( ) ) . unwrap ( ) ;
399
+ where_predicate
400
+ } ) ) ;
401
+
402
+ // Ensure that all generic types actually used are diffable and live for as long as the
403
+ // diffus lifetime.
404
+ for generic_ty_path in generic_types_used {
405
+ let where_predicate = quote ! ( #generic_ty_path : diffus:: Diffable <#impl_lifetime> + #impl_lifetime) ;
406
+ let where_predicate = syn:: parse :: < syn:: WherePredicate > ( where_predicate. into ( ) ) . unwrap ( ) ;
407
+
408
+ impl_diffable_where_clause. predicates . push ( where_predicate. clone ( ) ) ;
409
+ edited_ty_where_clause. predicates . push ( where_predicate. clone ( ) ) ;
410
+ }
411
+
412
+ Generics {
413
+ ty_generic_params,
414
+ edited_ty_generic_params, edited_ty_where_clause,
415
+ impl_diffable_generic_params, impl_diffable_where_clause,
416
+ impl_lifetime,
417
+ }
418
+ }
419
+
420
+ /// Collects all of the generic types used in a type including associated types.
421
+ fn collect_generic_types_used (
422
+ input_generics : & syn:: Generics ,
423
+ data : & syn:: Data ,
424
+ ) -> Vec < syn:: Path > {
425
+ let all_possible_fields: Vec < & syn:: Fields > = match * data {
426
+ syn:: Data :: Struct ( ref s) => vec ! [ & s. fields] ,
427
+ syn:: Data :: Enum ( ref e) => e. variants . iter ( ) . map ( |v| & v. fields ) . collect ( ) ,
428
+ syn:: Data :: Union ( ..) => Vec :: new ( ) , // unimplemented
429
+ } ;
430
+
431
+ let all_possible_types: Vec < & syn:: Type > = all_possible_fields. into_iter ( ) . flat_map ( |fields| match fields {
432
+ syn:: Fields :: Named ( ref fields) => fields. named . iter ( ) . map ( |f| & f. ty ) . collect ( ) ,
433
+ syn:: Fields :: Unnamed ( ref fields) => fields. unnamed . iter ( ) . map ( |f| & f. ty ) . collect ( ) ,
434
+ syn:: Fields :: Unit => Vec :: new ( ) ,
435
+ } ) . collect ( ) ;
436
+
437
+ let mut generic_types_used = Vec :: new ( ) ;
438
+ let mut remaining_types_to_check = all_possible_types. clone ( ) ;
439
+
440
+ while let Some ( type_to_check) = remaining_types_to_check. pop ( ) {
441
+ match * type_to_check {
442
+ syn:: Type :: Path ( ref path) => {
443
+ if let Some ( first_segment) = path. path . segments . first ( ) . map ( |s| & s. ident ) {
444
+ let first_segment: syn:: Ident = first_segment. clone ( ) . into ( ) ;
445
+
446
+ if input_generics. type_params ( ) . any ( |type_param| type_param. ident == first_segment) {
447
+ generic_types_used. push ( path. path . clone ( ) ) ;
448
+ }
449
+ }
450
+ } ,
451
+
452
+ syn:: Type :: Array ( ref array) => remaining_types_to_check. push ( & array. elem ) ,
453
+ syn:: Type :: Group ( ref group) => remaining_types_to_check. push ( & group. elem ) ,
454
+ syn:: Type :: Paren ( ref paren) => remaining_types_to_check. push ( & paren. elem ) ,
455
+ syn:: Type :: Ptr ( ref ptr) => remaining_types_to_check. push ( & ptr. elem ) ,
456
+ syn:: Type :: Reference ( ref reference) => remaining_types_to_check. push ( & reference. elem ) ,
457
+ syn:: Type :: Slice ( ref slice) => remaining_types_to_check. push ( & slice. elem ) ,
458
+ syn:: Type :: Tuple ( ref tuple) => remaining_types_to_check. extend ( tuple. elems . iter ( ) ) ,
459
+ syn:: Type :: Verbatim ( ..) |
460
+ syn:: Type :: ImplTrait ( ..) |
461
+ syn:: Type :: Infer ( ..) |
462
+ syn:: Type :: Macro ( ..) |
463
+ syn:: Type :: Never ( ..) |
464
+ syn:: Type :: TraitObject ( ..) |
465
+ syn:: Type :: BareFn ( ..) => ( ) ,
466
+ _ => ( ) , // unknown/unsupported type
467
+ }
468
+ }
469
+
470
+ generic_types_used
471
+ }
472
+ }
0 commit comments