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}