macro_tools/
generic_args.rs

1//!
2//! This module provides utilities to handle and manipulate generic arguments using the `syn` crate. It includes traits and functions for transforming, merging, and managing generic parameters within procedural macros, enabling seamless syntactic analysis and code generation.
3//!
4
5/// Define a private namespace for all its items.
6mod private
7{
8
9  /// A trait for converting a reference to an existing type into a `syn::AngleBracketedGenericArguments`.
10  ///
11  /// This trait provides a mechanism to transform various types that represent generic parameters,
12  /// such as `syn::Generics`, into a uniform `syn::AngleBracketedGenericArguments`. This is particularly
13  /// useful when working with Rust syntax trees in procedural macros, allowing for the manipulation
14  /// and merging of generic parameters from different syntactic elements.
15  pub trait IntoGenericArgs
16  {
17    /// Converts a reference of the implementing type into `syn::AngleBracketedGenericArguments`.
18    ///
19    /// This method should handle the conversion logic necessary to transform the implementing
20    /// type's generic parameter representations into the structured format required by
21    /// `syn::AngleBracketedGenericArguments`, which is commonly used to represent generic parameters
22    /// enclosed in angle brackets.
23    ///
24    /// # Returns
25    /// A new instance of `syn::AngleBracketedGenericArguments` representing the generic parameters
26    /// of the original type.
27    #[ allow( clippy::wrong_self_convention ) ]
28    fn into_generic_args( &self ) -> syn::AngleBracketedGenericArguments;
29  }
30
31  impl IntoGenericArgs for syn::Generics
32  {
33    fn into_generic_args( &self ) -> syn::AngleBracketedGenericArguments
34    {
35      let args = self.params.iter().map( | param |
36      {
37        match param
38        {
39          syn::GenericParam::Type( ty ) => syn::GenericArgument::Type( syn::Type::Path( syn::TypePath
40          {
41            qself: None,
42            path: ty.ident.clone().into(),
43          })),
44          syn::GenericParam::Lifetime( lifetime ) => syn::GenericArgument::Lifetime( lifetime.lifetime.clone() ),
45          syn::GenericParam::Const( const_param ) => syn::GenericArgument::Const( syn::Expr::Path( syn::ExprPath
46          {
47            attrs: vec![],
48            qself: None,
49            path: const_param.ident.clone().into(),
50          })),
51        }
52      }).collect();
53
54      syn::AngleBracketedGenericArguments
55      {
56        colon2_token: None,
57        lt_token: syn::token::Lt::default(),
58        args,
59        gt_token: syn::token::Gt::default(),
60      }
61    }
62  }
63
64  /// Merges two `syn::AngleBracketedGenericArguments` instances into a new one,
65  /// prioritizing lifetime parameters before other types of generic arguments.
66  ///
67  /// This function takes two references to `syn::AngleBracketedGenericArguments` and
68  /// categorizes their arguments into lifetimes and other types. It then combines
69  /// them such that all lifetimes from both instances precede any other arguments in the
70  /// resulting `syn::AngleBracketedGenericArguments` instance. This is particularly useful
71  /// for ensuring that the merged generics conform to typical Rust syntax requirements where
72  /// lifetimes are declared before other generic parameters.
73  ///
74  /// # Arguments
75  ///
76  /// * `a` - A reference to the first `syn::AngleBracketedGenericArguments` instance, containing one or more generic arguments.
77  /// * `b` - A reference to the second `syn::AngleBracketedGenericArguments` instance, containing one or more generic arguments.
78  ///
79  /// # Returns
80  ///
81  /// Returns a new `syn::AngleBracketedGenericArguments` instance containing the merged
82  /// arguments from both `a` and `b`, with lifetimes appearing first.
83  ///
84  /// # Examples
85  ///
86  /// ```
87  /// use macro_tools::{
88  ///   generic_args,
89  ///   syn::{parse_quote, AngleBracketedGenericArguments},
90  /// };
91  ///
92  /// let a : AngleBracketedGenericArguments = parse_quote! { <'a, T: Clone, U: Default> };
93  /// let b : AngleBracketedGenericArguments = parse_quote! { <'b, V: core::fmt::Debug> };
94  /// let merged = generic_args::merge(&a, &b);
95  ///
96  /// let expected: AngleBracketedGenericArguments = parse_quote! { <'a, 'b, T: Clone, U: Default, V: core::fmt::Debug> };
97  /// assert_eq!(merged, expected);
98  /// ```
99  ///
100  /// This example demonstrates how lifetimes `'a` and `'b` are placed before other generic parameters
101  /// like `T`, `U`, and `V` in the merged result, adhering to the expected syntax order in Rust generics.
102  #[ must_use ]
103  pub fn merge
104  (
105    a : &syn::AngleBracketedGenericArguments,
106    b : &syn::AngleBracketedGenericArguments
107  ) -> syn::AngleBracketedGenericArguments
108  {
109    let mut lifetimes : syn::punctuated::Punctuated< syn::GenericArgument, syn::token::Comma > = syn::punctuated::Punctuated::new();
110    let mut others : syn::punctuated::Punctuated< syn::GenericArgument, syn::token::Comma > = syn::punctuated::Punctuated::new();
111
112    // Function to categorize and collect arguments into lifetimes and others
113    let mut categorize_and_collect = |args : &syn::punctuated::Punctuated<syn::GenericArgument, syn::token::Comma>|
114    {
115      for arg in args
116      {
117        match arg
118        {
119          syn::GenericArgument::Lifetime( _ ) => lifetimes.push( arg.clone() ),
120          _ => others.push( arg.clone() ),
121        }
122      }
123    };
124
125    // Categorize and collect from both input arguments
126    categorize_and_collect( &a.args );
127    categorize_and_collect( &b.args );
128
129    // Combine lifetimes and other arguments into final merged arguments
130    let mut args = syn::punctuated::Punctuated::new();
131    args.extend( lifetimes );
132    args.extend( others );
133
134    syn::AngleBracketedGenericArguments
135    {
136      colon2_token: None, // Adjust if needed based on context
137      lt_token: syn::token::Lt::default(),
138      args,
139      gt_token: syn::token::Gt::default(),
140    }
141  }
142
143}
144
145#[ doc( inline ) ]
146#[ allow( unused_imports ) ]
147pub use own::*;
148
149/// Own namespace of the module.
150#[ allow( unused_imports ) ]
151pub mod own
152{
153  #[ allow( clippy::wildcard_imports ) ]
154  use super::*;
155
156  #[ doc( inline ) ]
157  pub use orphan::*;
158  #[ doc( inline ) ]
159  pub use private::
160  {
161    merge,
162  };
163}
164
165/// Orphan namespace of the module.
166#[ allow( unused_imports ) ]
167pub mod orphan
168{
169  #[ allow( clippy::wildcard_imports ) ]
170  use super::*;
171  #[ doc( inline ) ]
172  pub use exposed::*;
173  #[ doc( inline ) ]
174  pub use private::
175  {
176    IntoGenericArgs,
177  };
178}
179
180/// Exposed namespace of the module.
181#[ allow( unused_imports ) ]
182pub mod exposed
183{
184  use super::*;
185  pub use super::super::generic_args;
186
187  #[ doc( inline ) ]
188  #[ allow( unused_imports ) ]
189  pub use super::
190  {
191    prelude::*,
192  };
193}
194
195/// Prelude to use essentials: `use my_module::prelude::*`.
196#[ allow( unused_imports ) ]
197pub mod prelude
198{
199  use super::*;
200}