macro_tools/generic_params.rs
1//!
2//! Functions and structures to handle and manipulate generic parameters using the `syn` crate. It's designed to support macro-driven code generation by simplifying, merging, extracting, and decomposing `syn::Generics`.
3//!
4
5/// Define a private namespace for all its items.
6mod private
7{
8 #[ allow( clippy::wildcard_imports ) ]
9 use crate::*;
10 use crate::IterTrait;
11 // use iter_tools::IterTrait;
12
13 /// A `GenericsWithWhere` struct to handle the parsing of Rust generics with an explicit `where` clause.
14 ///
15 /// This wrapper addresses the limitation in the `syn` crate where parsing `Generics` directly from a `ParseStream`
16 /// does not automatically handle associated `where` clauses. By integrating `where` clause parsing into the
17 /// `GenericsWithWhere`, this struct provides a seamless way to capture both the generics and their constraints
18 /// in scenarios where the `where` clause is crucial for type constraints and bounds in Rust macros and code generation.
19 ///
20 /// Usage:
21 ///
22 /// ```
23 /// let parsed_generics : macro_tools::GenericsWithWhere
24 /// = syn::parse_str( "< T : Clone, U : Default = Default1 > where T : Default" ).unwrap();
25 /// assert!( parsed_generics.generics.params.len() == 2 );
26 /// assert!( parsed_generics.generics.where_clause.is_some() );
27 /// ```
28 ///
29
30 #[ derive( Debug ) ]
31 pub struct GenericsWithWhere
32 {
33 /// Syn's generics parameters.
34 pub generics : syn::Generics,
35 }
36
37 impl GenericsWithWhere
38 {
39 /// Unwraps the `GenericsWithWhere` to retrieve the inner `syn::Generics`.
40 #[ must_use ]
41 pub fn unwrap( self ) -> syn::Generics
42 {
43 self.generics
44 }
45
46 /// Parses a string to a `GenericsWithWhere`, specifically designed to handle generics syntax with where clauses effectively.
47 /// # Errors
48 /// qqq: doc
49 pub fn parse_from_str( s : &str ) -> syn::Result< GenericsWithWhere >
50 {
51 syn::parse_str::< GenericsWithWhere >( s )
52 }
53 }
54
55 impl syn::parse::Parse for GenericsWithWhere
56 {
57 fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self >
58 {
59 let generics : syn::Generics = input.parse()?;
60 let where_clause : Option< syn::WhereClause > = input.parse()?;
61
62 let mut generics_clone = generics.clone();
63 generics_clone.where_clause = where_clause;
64
65 Ok( GenericsWithWhere
66 {
67 generics : generics_clone,
68 })
69 }
70 }
71
72 impl quote::ToTokens for GenericsWithWhere
73 {
74 fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream )
75 {
76 self.generics.to_tokens( tokens );
77 }
78 }
79
80 impl From<GenericsWithWhere> for syn::Generics
81 {
82 fn from( g : GenericsWithWhere ) -> Self
83 {
84 g.generics
85 }
86 }
87
88 impl From<syn::Generics> for GenericsWithWhere
89 {
90 fn from( generics : syn::Generics ) -> Self
91 {
92 GenericsWithWhere { generics }
93 }
94 }
95
96 /// Merges two `syn::Generics` instances into a new one.
97 ///
98 /// This function takes two references to `syn::Generics` and combines their
99 /// type parameters and where clauses into a new `syn::Generics` instance. If
100 /// both instances have where clauses, the predicates of these clauses are merged
101 /// into a single where clause.
102 ///
103 /// # Arguments
104 ///
105 /// * `a` - A reference to the first `syn::Generics` instance.
106 /// * `b` - A reference to the second `syn::Generics` instance.
107 ///
108 /// # Returns
109 ///
110 /// Returns a new `syn::Generics` instance containing the merged type parameters
111 /// and where clauses from `a` and `b`.
112 ///
113 /// # Examples
114 ///
115 ///
116 /// # use `syn::{Generics`, `parse_quote`};
117 ///
118 /// let mut `generics_a` : `syn::Generics` = `parse_quote`!{ < T : Clone, U : Default > };
119 /// `generics_a.where_clause` = `parse_quote`!{ where T : Default };
120 /// let mut `generics_b` : `syn::Generics` = `parse_quote`!{ < V : `core::fmt::Debug` > };
121 /// `generics_b.where_clause` = `parse_quote`!{ where V : Sized };
122 /// let got = `generic_params::merge`( &`generics_a`, &`generics_b` );
123 ///
124 /// let mut exp : `syn::Generics` = `parse_quote`!
125 /// {
126 /// < T : Clone, U : Default, V : `core::fmt::Debug` >
127 /// };
128 /// `exp.where_clause` = `parse_quote`!
129 /// {
130 /// where
131 /// T : Default,
132 /// V : Sized
133 /// };
134 ///
135 /// `assert_eq`!( got, exp );
136 #[ must_use ]
137 #[ allow( clippy::default_trait_access ) ]
138 pub fn merge( a : &syn::Generics, b : &syn::Generics ) -> syn::Generics
139 {
140
141 let mut result = syn::Generics
142 {
143 params : Default::default(),
144 where_clause : None,
145 lt_token : Some( syn::token::Lt::default() ),
146 gt_token : Some( syn::token::Gt::default() ),
147 };
148
149 // Merge params
150 // result.params.extend( a.params.iter().chain( b.params.iter() ) );
151 for param in &a.params
152 {
153 result.params.push( param.clone() );
154 }
155 for param in &b.params
156 {
157 result.params.push( param.clone() );
158 }
159
160 // Merge where clauses
161 result.where_clause = match( &a.where_clause, &b.where_clause )
162 {
163 ( Some( a_clause ), Some( b_clause ) ) =>
164 {
165 let mut merged_where_clause = syn::WhereClause
166 {
167 where_token: a_clause.where_token,
168 predicates: a_clause.predicates.clone(),
169 };
170 for predicate in &b_clause.predicates
171 {
172 merged_where_clause.predicates.push( predicate.clone() );
173 }
174 Some( merged_where_clause )
175 },
176 ( Some( a_clause ), None ) => Some( a_clause.clone() ),
177 ( None, Some( b_clause ) ) => Some( b_clause.clone() ),
178 _ => None,
179 };
180
181 result
182 }
183
184 /// Extracts parameter names from the given `Generics`,
185 /// dropping bounds, defaults, and the where clause.
186 ///
187 /// This function simplifies the generics to include only the names of the type parameters,
188 /// lifetimes, and const parameters, without any of their associated bounds or default values.
189 /// The resulting `Generics` will have an empty where clause.
190 ///
191 /// # Arguments
192 ///
193 /// * `generics` - The `Generics` instance from which to extract parameter names.
194 ///
195 /// # Returns
196 ///
197 /// Returns a new `Generics` instance containing only the names of the parameters.
198 ///
199 /// # Examples
200 ///
201 /// ```rust
202 /// # use macro_tools::syn::parse_quote;
203 ///
204 /// let mut generics : syn::Generics = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > };
205 /// generics.where_clause = parse_quote!{ where T: core::fmt::Debug };
206 /// // let generics : Generics = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > where T: core::fmt::Debug };
207 /// let simplified_generics = macro_tools::generic_params::only_names( &generics );
208 ///
209 /// assert_eq!( simplified_generics.params.len(), 4 ); // Contains T, U, 'a, and N
210 /// assert!( simplified_generics.where_clause.is_none() ); // Where clause is removed
211 /// ```
212 #[ allow( clippy::default_trait_access ) ]
213 #[ must_use ]
214 pub fn only_names( generics : &syn::Generics ) -> syn::Generics
215 {
216 // use syn::{ Generics, GenericParam, LifetimeDef, TypeParam, ConstParam };
217 use syn::{ Generics, GenericParam, LifetimeParam, TypeParam, ConstParam };
218
219 let result = Generics
220 {
221 params : generics.params.iter().map( | param | match param
222 {
223 GenericParam::Type( TypeParam { ident, .. } ) => GenericParam::Type( TypeParam
224 {
225 attrs : Vec::new(),
226 ident : ident.clone(),
227 colon_token : None,
228 bounds : Default::default(),
229 eq_token : None,
230 default : None,
231 }),
232 GenericParam::Lifetime( LifetimeParam { lifetime, .. } ) => GenericParam::Lifetime( LifetimeParam
233 {
234 attrs : Vec::new(),
235 lifetime : lifetime.clone(),
236 colon_token : None,
237 bounds : Default::default(),
238 }),
239 GenericParam::Const( ConstParam { ident, ty, .. } ) => GenericParam::Const( ConstParam
240 {
241 attrs : Vec::new(),
242 const_token : Default::default(),
243 ident : ident.clone(),
244 colon_token : Default::default(),
245 ty : ty.clone(),
246 eq_token : Default::default(),
247 default : None,
248 }),
249 }).collect(),
250 where_clause : None,
251 lt_token : generics.lt_token,
252 gt_token : generics.gt_token,
253 };
254
255 result
256 }
257
258 /// Extracts the names of type parameters, lifetimes, and const parameters from the given `Generics`.
259 ///
260 /// This function returns an iterator over the names of the parameters in the `Generics`,
261 /// which can be useful for generating code that requires just the names of the parameters
262 /// without their associated bounds or default values.
263 ///
264 /// # Arguments
265 ///
266 /// * `generics` - The `Generics` instance from which to extract parameter names.
267 ///
268 /// # Returns
269 ///
270 /// Returns an iterator over the names of the parameters.
271 ///
272 /// # Examples
273 ///
274 /// ```rust
275 /// # use macro_tools::syn::parse_quote;
276 ///
277 /// let generics : syn::Generics = parse_quote!
278 /// {
279 /// < T : Clone + Default, U, 'a, const N : usize >
280 /// };
281 /// let names : Vec< _ > = macro_tools::generic_params::names( &generics ).collect();
282 ///
283 /// assert_eq!( names, vec!
284 /// [
285 /// &syn::Ident::new( "T", proc_macro2::Span::call_site() ),
286 /// &syn::Ident::new( "U", proc_macro2::Span::call_site() ),
287 /// &syn::Ident::new( "a", proc_macro2::Span::call_site() ),
288 /// &syn::Ident::new( "N", proc_macro2::Span::call_site() )
289 /// ]);
290 /// ```
291 #[ must_use ]
292 pub fn names( generics : &syn::Generics )
293 -> impl IterTrait< '_, &syn::Ident >
294 // -> std::iter::Map
295 // <
296 // syn::punctuated::Iter< 'a, syn::GenericParam >,
297 // impl FnMut( &'a syn::GenericParam ) -> &'a syn::Ident + 'a,
298 // >
299 {
300 generics.params.iter().map( | param | match param
301 {
302 syn::GenericParam::Type( type_param ) => &type_param.ident,
303 syn::GenericParam::Lifetime( lifetime_def ) => &lifetime_def.lifetime.ident,
304 syn::GenericParam::Const( const_param ) => &const_param.ident,
305 })
306 }
307
308 /// Decomposes `syn::Generics` into components suitable for different usage contexts in Rust implementations,
309 /// specifically focusing on different requirements for `impl` blocks and type definitions.
310 ///
311 /// This function prepares three versions of the generics:
312 /// - One preserving the full structure for `impl` declarations.
313 /// - One simplified for type definitions, removing bounds and defaults from type and const parameters, retaining only identifiers.
314 /// - One for the where clauses, if present, ensuring they are correctly punctuated.
315 ///
316 /// This helps in situations where you need different representations of generics for implementing traits,
317 /// defining types, or specifying trait bounds and conditions.
318 ///
319 /// This function is similar to `syn::Generics::split_for_impl`, which also splits generics into components
320 /// suitable for `impl` blocks and type definitions. However, `split_for_impl` wraps the tokens in `<>`, which
321 /// can reduce the flexibility of the results. The `decompose` function provides more control over the output
322 /// by not wrapping the tokens, allowing for more precise usage in macros and other contexts.
323 /// Additionally, `decompose` returns an extra component with the generics including defaults, which is often
324 /// in demand for certain macro or code generation tasks.
325 ///
326 /// # Examples
327 ///
328 /// ```rust
329 /// let code : syn::Generics = syn::parse_quote!{ <'a, T, const N : usize, U : Trait1> };
330 /// let ( generics_with_defaults, generics_for_impl, generics_for_ty, generics_where ) = macro_tools::generic_params::decompose( &code );
331 ///
332 /// // Use in a macro for generating code
333 /// macro_tools::qt!
334 /// {
335 /// impl < #generics_for_impl > MyTrait for Struct1 < #generics_for_ty >
336 /// where
337 /// #generics_where
338 /// {
339 /// // implementation details...
340 /// }
341 /// };
342 /// ```
343 ///
344 /// # Arguments
345 ///
346 /// * `generics` - A reference to the `syn::Generics` to be decomposed.
347 ///
348 /// # Returns
349 ///
350 /// Returns a tuple containing:
351 /// - `syn::punctuated::Punctuated<syn::GenericParam, syn::token::Comma>`: Original generics with defaults, used where full specification is needed.
352 /// - `syn::punctuated::Punctuated<syn::GenericParam, syn::token::Comma>`: Generics for `impl` blocks, retaining bounds but no defaults.
353 /// - `syn::punctuated::Punctuated<syn::GenericParam, syn::token::Comma>`: Simplified generics for type definitions, only identifiers.
354 /// - `syn::punctuated::Punctuated<syn::WherePredicate, syn::token::Comma>`: Where clauses, properly punctuated for use in where conditions.
355 ///
356 /// # Differences from `syn::Generics::split_for_impl`
357 ///
358 /// While both `decompose` and `split_for_impl` functions split generics into components for `impl` blocks, type definitions, and where clauses,
359 /// there are key differences:
360 /// - `split_for_impl` wraps the generics in `<>`, which can be limiting when you need to use the generics in a different context or format.
361 /// - `decompose` provides raw punctuated generic parameters, offering greater flexibility and control over the output format.
362 /// - `decompose` returns an extra component with the generics including defaults, which is often needed for certain macro or code generation tasks.
363 ///
364 /// # Example of function signature using `decompose`
365 ///
366 /// ```rust
367 /// use macro_tools::{ syn, proc_macro2, qt };
368 ///
369 /// fn generate_unit
370 /// (
371 /// item_name : &syn::Ident,
372 /// generics_with_defaults : syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >,
373 /// generics_impl : syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >,
374 /// generics_ty : syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >,
375 /// generics_where: syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >,
376 /// )
377 /// -> proc_macro2::TokenStream
378 /// {
379 /// qt!
380 /// {
381 /// #[ automatically_derived ]
382 /// impl< #generics_impl > From< i32 > for #item_name< #generics_ty >
383 /// where
384 /// #generics_where
385 /// {
386 /// #[ inline ]
387 /// fn from( src : i32 ) -> Self
388 /// {
389 /// Wrap( src )
390 /// }
391 /// }
392 /// }
393 /// }
394 /// ```
395 ///
396 #[ allow( clippy::type_complexity ) ]
397 #[ must_use ]
398 pub fn decompose
399 (
400 generics : &syn::Generics,
401 )
402 ->
403 (
404 syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >,
405 syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >,
406 syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >,
407 syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >,
408 )
409 {
410
411 let mut generics_with_defaults = generics.params.clone();
412 punctuated::ensure_trailing_comma( &mut generics_with_defaults );
413
414 let mut generics_for_impl = syn::punctuated::Punctuated::new();
415 let mut generics_for_ty = syn::punctuated::Punctuated::new();
416
417 // Process each generic parameter
418 for param in &generics.params
419 {
420 match param
421 {
422 syn::GenericParam::Type( type_param ) =>
423 {
424 // Retain bounds for generics_for_impl, remove defaults
425 let impl_param = syn::GenericParam::Type( syn::TypeParam
426 {
427 attrs : vec![],
428 ident : type_param.ident.clone(),
429 colon_token : type_param.colon_token,
430 bounds : type_param.bounds.clone(),
431 eq_token : None, // Remove default token
432 default : None, // Remove default value
433 } );
434 generics_for_impl.push_value( impl_param );
435 generics_for_impl.push_punct( syn::token::Comma::default() );
436
437 // Simplify for generics_for_ty by removing all except identifiers
438 let ty_param = syn::GenericParam::Type( syn::TypeParam
439 {
440 attrs : vec![],
441 ident : type_param.ident.clone(),
442 colon_token : None,
443 bounds : syn::punctuated::Punctuated::new(),
444 eq_token : None,
445 default : None,
446 } );
447 generics_for_ty.push_value( ty_param );
448 generics_for_ty.push_punct( syn::token::Comma::default() );
449 },
450 syn::GenericParam::Const( const_param ) =>
451 {
452 // Simplify const parameters by removing all details except the identifier
453 let impl_param = syn::GenericParam::Const( syn::ConstParam
454 {
455 attrs : vec![],
456 const_token : const_param.const_token,
457 ident : const_param.ident.clone(),
458 colon_token : const_param.colon_token,
459 ty : const_param.ty.clone(),
460 eq_token : None,
461 default : None,
462 } );
463 generics_for_impl.push_value( impl_param );
464 generics_for_impl.push_punct( syn::token::Comma::default() );
465
466 let ty_param = syn::GenericParam::Type( syn::TypeParam
467 {
468 attrs : vec![],
469 ident : const_param.ident.clone(),
470 colon_token : None,
471 bounds : syn::punctuated::Punctuated::new(),
472 eq_token : None,
473 default : None,
474 });
475 generics_for_ty.push_value( ty_param );
476 generics_for_ty.push_punct( syn::token::Comma::default() );
477 },
478 syn::GenericParam::Lifetime( lifetime_param ) =>
479 {
480 // Lifetimes are added as-is to generics_for_impl and without bounds to generics_for_ty
481 generics_for_impl.push_value( syn::GenericParam::Lifetime( lifetime_param.clone() ) );
482 generics_for_impl.push_punct( syn::token::Comma::default() );
483
484 let ty_param = syn::GenericParam::Lifetime( syn::LifetimeParam
485 {
486 attrs : vec![],
487 lifetime : lifetime_param.lifetime.clone(),
488 colon_token : None,
489 bounds : syn::punctuated::Punctuated::new(),
490 });
491 generics_for_ty.push_value( ty_param );
492 generics_for_ty.push_punct( syn::token::Comma::default() );
493 }
494 }
495 }
496
497 // Clone where predicates if present, ensuring they end with a comma
498 let generics_where = if let Some( where_clause ) = &generics.where_clause
499 {
500 let mut predicates = where_clause.predicates.clone();
501 punctuated::ensure_trailing_comma( &mut predicates );
502 predicates
503 }
504 else
505 {
506 syn::punctuated::Punctuated::new()
507 };
508
509 ( generics_with_defaults, generics_for_impl, generics_for_ty, generics_where )
510 }
511
512}
513
514#[ doc( inline ) ]
515#[ allow( unused_imports ) ]
516pub use own::*;
517
518#[ allow( unused_imports ) ]
519/// Own namespace of the module.
520pub mod own
521{
522 #[ allow( clippy::wildcard_imports ) ]
523 use super::*;
524
525 #[ doc( inline ) ]
526 pub use orphan::*;
527 #[ doc( inline ) ]
528 pub use private::
529 {
530 merge,
531 only_names,
532 names,
533 decompose,
534 };
535}
536
537/// Orphan namespace of the module.
538#[ allow( unused_imports ) ]
539pub mod orphan
540{
541 #[ allow( clippy::wildcard_imports ) ]
542 use super::*;
543 #[ doc( inline ) ]
544 pub use exposed::*;
545 #[ doc( inline ) ]
546 pub use private::
547 {
548 GenericsWithWhere,
549 };
550}
551
552/// Exposed namespace of the module.
553#[ allow( unused_imports ) ]
554pub mod exposed
555{
556 use super::*;
557 pub use super::super::generic_params;
558
559 #[ doc( inline ) ]
560 #[ allow( unused_imports ) ]
561 pub use super::
562 {
563 prelude::*,
564 };
565}
566
567/// Prelude to use essentials: `use my_module::prelude::*`.
568#[ allow( unused_imports ) ]
569pub mod prelude
570{
571 use super::*;
572}