trait_gen/lib.rs
1// Copyright (c) 2025 Redglyph (@gmail.com). All Rights Reserved.
2//
3// Publicly exposed macros.
4
5//! This crate provides attribute macros that generate the attached implementation for all the
6//! types given in argument. It was first intended for trait implementations, hence the crate name,
7//! but it can also be used for any generic implementation.
8//!
9//! ## Usage
10//! The attribute is placed before the pseudo-generic code to implement. The _generic arguments_
11//! are given first, followed by a right arrow (`->`) and a list of types that will replace the
12//! argument in the generated implementations:
13//!
14//! ```rust
15//! # use trait_gen::trait_gen;
16//! # struct Type1; struct Type2; struct Type3;
17//! # trait Trait {}
18//! #[trait_gen(T -> Type1, Type2, Type3)]
19//! impl Trait for T {
20//! // ...
21//! }
22//! ```
23//!
24//! The attribute macro successively substitutes the generic argument `T` in the code with
25//! the given types (`Type1`, `Type2`, `Type3`) to generate each implementation.
26//!
27//! All the [type paths](https://doc.rust-lang.org/reference/paths.html#paths-in-types) beginning
28//! with `T` in the code have that part replaced. For example, `T::default()` generates
29//! `Type1::default()`, `Type2::default()` and so on, but `super::T` is unchanged. Similarly, all
30//! the [types](https://doc.rust-lang.org/reference/types.html) including `T` in the code have that
31//! part replaced; for example, `&T` or `Box<T>`.
32//!
33//! The compiler will trigger an error if the resulting code is wrong. For example
34//! `#[trait_gen(T -> u64, f64)]` cannot be applied to `let x: T = 0;` because `0` is not a valid
35//! floating-point literal.
36//!
37//! Finally, the actual type of `T` replaces any occurrence of `${T}` in doc comments, macros, and
38//! string literals.
39//!
40//! _Notes:_
41//! - _Using the letter "T" is not mandatory; any type path will do. For example, `g::Type` is fine
42//! too. But to make it easy to read and similar to a generic implementation, short upper-case identifiers
43//! are preferred._
44//! - _If a `<..>` is required in the generic argument, the
45//! [turbofish syntax](https://doc.rust-lang.org/reference/paths.html#r-paths.expr.turbofish) must be used.
46//! For example, use `#[trait_gen(T::<U> -> ...)` and not `#[trait_gen(T<U> -> ...)`._
47//! - _`type_gen` is a synonym attribute that can be used instead of `trait_gen`. This can be disabled with
48//! the `no_type_gen` feature, in case it conflicts with another 3rd-party attribute._
49//! - _There is no escape code to avoid the substitution in string literals; if you need `${T}` for another
50//! purpose and you don't want it to be replaced, you can use this work-around:
51//! `#[doc = concat!("my ${", "T} variable")]`. Or you can choose another generic argument, like `U` or `my::T`._
52//! - _More complex formats with several arguments and conditions are shown in later examples._
53//!
54//! Here is a simple example:
55//!
56//! ```rust
57//! # use trait_gen::trait_gen;
58//! # trait MyLog { fn my_log2(self) -> u32; }
59//! #[trait_gen(T -> u8, u16, u32, u64, u128)]
60//! impl MyLog for T {
61//! fn my_log2(self) -> u32 {
62//! T::BITS - 1 - self.leading_zeros()
63//! }
64//! }
65//! ```
66//!
67//! The `trait_gen` attribute generates the following code by replacing `T` with the types given as
68//! arguments:
69//!
70//! ```rust
71//! # trait MyLog { fn my_log2(self) -> u32; }
72//! impl MyLog for u8 {
73//! fn my_log2(self) -> u32 {
74//! u8::BITS - 1 - self.leading_zeros()
75//! }
76//! }
77//!
78//! impl MyLog for u16 {
79//! fn my_log2(self) -> u32 {
80//! u16::BITS - 1 - self.leading_zeros()
81//! }
82//! }
83//!
84//! // ... and so on for the remaining types
85//! ```
86//!
87//! ## Compositions
88//! `trait_gen` also replaces the content of inner attributes, so it's possible to chain them
89//! and extend the above example to references and smart pointers for all the `T` types:
90//!
91//! ```rust
92//! # use trait_gen::trait_gen;
93//! # trait MyLog { fn my_log2(self) -> u32; }
94//! # #[trait_gen(T -> u8, u16, u32, u64, u128)]
95//! # impl MyLog for T {
96//! # fn my_log2(self) -> u32 {
97//! # T::BITS - 1 - self.leading_zeros()
98//! # }
99//! # }
100//! #[trait_gen(T -> u8, u16, u32, u64, u128)]
101//! #[trait_gen(U -> &T, &mut T, Box<T>)]
102//! impl MyLog for U {
103//! /// Logarithm base 2 for `${U}`
104//! fn my_log2(self) -> u32 {
105//! MyLog::my_log2(*self)
106//! }
107//! }
108//! ```
109//!
110//! ## Tuples and Conditional Generation
111//! A more concise format can be used when several arguments share the type lists (in other
112//! words, when we need _permutations with repetitions_, or _tuples_):
113//!
114//! ```rust,ignore
115//! #[trait_gen(T, U -> u8, u16, u32)]
116//! ```
117//!
118//! In the following example, we also show the conditional attribute `trait_gen_if`, which
119//! offers more flexibility in the implementations. The condition has the general format
120//! `<argument> in <types>`, or its negation, `!<argument> in <types>`. The code is respectively
121//! included or skipped when the argument is identical to one of the types.
122//!
123//! ```rust
124//! use trait_gen::{trait_gen, trait_gen_if};
125//!
126//! #[derive(Clone, PartialEq, Debug)]
127//! struct Wrapper<T>(T);
128//!
129//! #[trait_gen(T, U -> u8, u16, u32)]
130//! // The types T and U must be different to avoid the compilation error
131//! // "conflicting implementation in crate `core`: impl<T> From<T> for T"
132//! #[trait_gen_if(!T in U)]
133//! impl From<Wrapper<U>> for Wrapper<T> {
134//! /// converts Wrapper<${U}> to Wrapper<${T}>
135//! fn from(value: Wrapper<U>) -> Self {
136//! Wrapper(T::try_from(value.0)
137//! .expect(&format!("overflow when converting {} to ${T}", value.0)))
138//! }
139//! }
140//! ```
141//!
142//! That will give us all the conversions from/to `u8`, `u16`, and `u32`, except from the
143//! same type since they're already covered by a blanket implementation in the standard library.
144//! `trait_gen_if` is also very useful for selecting constants or removing methods depending on the
145//! implementated type.
146//!
147//! _Notes:_
148//! - _The number of generic arguments is not limited in this particular form, though it's arguably
149//! hard to find relevant cases where more than two are required._
150//! - _We've seen earlier that `type_gen` was a synonym of `trait_gen`. For the sake of
151//! coherency, a `type_gen_if` is provided as a synonym of `trait_gen_if`, too._
152//!
153//! ## Other Permutations
154//! The implementation above could have been written more concisely with a _2-permutation_, where
155//! `T != U`:
156//!
157//! ```rust
158//! # use trait_gen::trait_gen;
159//! #
160//! # #[derive(Clone, PartialEq, Debug)]
161//! # struct Wrapper<T>(T);
162//! #
163//! #[trait_gen(T != U -> u8, u16, u32)]
164//! impl From<Wrapper<U>> for Wrapper<T> {
165//! /// converts Wrapper<${U}> to Wrapper<${T}>
166//! fn from(value: Wrapper<U>) -> Self {
167//! Wrapper(T::try_from(value.0)
168//! .expect(&format!("overflow when converting {} to ${T}", value.0)))
169//! }
170//! }
171//! ```
172//!
173//! If we want to generate all the conversions from smaller integers to bigger integers,
174//! similarly to what is done in the [standard library](https://github.com/rust-lang/rust/blob/1.86.0/library/core/src/convert/num.rs#L514-L526)
175//! (with a cascade of declarative macros), we can use a _2-permutation with strict order_,
176//! meaning that `index(T) < index(U)`—remember we can't convert to the same type
177//! because it conflicts with the blanket implementation in `core`.
178//!
179//! This will generate the code for `(T, U)` = `(u8, u16)`, `(u8, u32)`, and `(u16, u32)`
180//! (picture a triangle):
181//!
182//! ```rust
183//! # use trait_gen::trait_gen;
184//! #
185//! # #[derive(Clone, PartialEq, Debug)]
186//! # struct Wrapper<T>(T);
187//! #
188//! #[trait_gen(T < U -> u8, u16, u32)]
189//! impl From<Wrapper<T>> for Wrapper<U> {
190//! /// converts Wrapper<${T}> to Wrapper<${U}>
191//! fn from(value: Wrapper<T>) -> Self {
192//! Wrapper(U::from(value.0))
193//! }
194//! }
195//! ```
196//!
197//!
198//! The _non-strict order_, where `index(T) <= index(U)`, also exists for cases like
199//! adding from another integer which has a smaller or equal length. This will generate
200//! the code for `(T, U)` = `(u8, u8)`, `(u8, u16)`, `(u8, u32)`, `(u16, u16)`, `(u16, u32)`,
201//! and `(u32, u32)`.
202//!
203//! ```rust
204//! # use std::ops::Add;
205//! # use trait_gen::trait_gen;
206//! #
207//! # #[derive(Clone, PartialEq, Debug)]
208//! # struct Wrapper<T>(T);
209//! #
210//! #[trait_gen(T <= U -> u8, u16, u32)]
211//! impl Add<Wrapper<T>> for Wrapper<U> {
212//! type Output = Wrapper<U>;
213//!
214//! fn add(self, rhs: Wrapper<T>) -> Self::Output {
215//! Wrapper::<U>(self.0 + <U>::from(rhs.0))
216//! }
217//! }
218//! ```
219//!
220//! _Notes:_
221//! - _`!=`, `<`, and `<=` are limited to two generic arguments._
222//!
223//! That covers all the forms of these attributes. For more examples, look at the crate's
224//! [integration tests](https://github.com/blueglyph/trait_gen/blob/v2.0.0/tests/integration.rs).
225//!
226//! ## Limitations
227//!
228//! * The procedural macro of the `trait_gen` attribute can't handle scopes, so it doesn't support any
229//! type declaration with the same literal as the generic argument. For instance, this code fails to compile
230//! because of the generic function:
231//!
232//! ```rust, ignore
233//! #[trait_gen(T -> u64, i64, u32, i32)]
234//! impl AddMod for T {
235//! type Output = T;
236//!
237//! fn add_mod(self, rhs: Self, modulo: Self) -> Self::Output {
238//! fn int_mod<T: Num> (a: T, m: T) -> T { // <== ERROR, conflicting 'T'
239//! a % m
240//! }
241//! int_mod(self + rhs, modulo)
242//! }
243//! }
244//! ```
245//!
246//! * The generic argument must be a [type path](https://doc.rust-lang.org/reference/paths.html#paths-in-types);
247//! it cannot be a more complex type like a reference or a slice. So you can use `g::T<U> -> ...`
248//! but not `&T -> ...`.
249
250mod tests;
251mod lib_macros;
252mod subst;
253mod args;
254mod utils;
255
256use proc_macro::TokenStream;
257use proc_macro_error2::proc_macro_error;
258use crate::lib_macros::{macro_trait_gen, macro_trait_gen_if};
259
260//==============================================================================
261// Substitution attributes
262
263/// Generates the attached implementation code for all the types given in argument.
264///
265/// The attribute is placed before the pseudo-generic code to implement. The _generic arguments_
266/// are given first, followed by a right arrow (`->`) and a list of types that will replace the
267/// argument in the generated implementations:
268///
269/// ```rust
270/// # use trait_gen::trait_gen;
271/// # struct Type1; struct Type2; struct Type3;
272/// # trait Trait {}
273/// #[trait_gen(T -> Type1, Type2, Type3)]
274/// impl Trait for T {
275/// // ...
276/// }
277/// ```
278///
279/// The attribute macro successively substitutes the generic argument `T` in the code with
280/// the given types (`Type1`, `Type2`, `Type3`) to generate each implementation.
281///
282/// All the [type paths](https://doc.rust-lang.org/reference/paths.html#paths-in-types) beginning
283/// with `T` in the code have that part replaced. For example, `T::default()` generates
284/// `Type1::default()`, `Type2::default()` and so on, but `super::T` is unchanged. Similarly, all
285/// the [types](https://doc.rust-lang.org/reference/types.html) including `T` in the code have that
286/// part replaced; for example, `&T` or `Box<T>`.
287///
288/// The compiler will trigger an error if the resulting code is wrong. For example
289/// `#[trait_gen(T -> u64, f64)]` cannot be applied to `let x: T = 0;` because `0` is not a valid
290/// floating-point literal.
291///
292/// Finally, the actual type of `T` replaces any occurrence of `${T}` in doc comments, macros, and
293/// string literals.
294///
295/// _Notes:_
296/// - _Using the letter "T" is not mandatory; any type path will do. For example, `g::Type` is fine
297/// too. But to make it easy to read and similar to a generic implementation, short upper-case identifiers
298/// are preferred._
299/// - _If a `<..>` is required in the generic argument, the
300/// [turbofish syntax](https://doc.rust-lang.org/reference/paths.html#r-paths.expr.turbofish) must be used.
301/// For example, use `T::<U>` and not `T<U>`._
302/// - _`type_gen` is a synonym attribute that can be used instead of `trait_gen`. This can be disabled with
303/// the `no_type_gen` feature, in case it conflicts with another 3rd-party attribute._
304///
305/// See the [crate documentation](crate) for more details.
306///
307/// ## Example
308///
309/// ```rust
310/// # use trait_gen::trait_gen;
311/// # trait MyLog { fn my_log2(self) -> u32; }
312/// #[trait_gen(T -> u8, u16, u32, u64, u128)]
313/// impl MyLog for T {
314/// /// Logarithm base 2 for `${T}`
315/// fn my_log2(self) -> u32 {
316/// T::BITS - 1 - self.leading_zeros()
317/// }
318/// }
319///
320/// #[trait_gen(T -> u8, u16, u32, u64, u128)]
321/// #[trait_gen(U -> &T, &mut T, Box<T>)]
322/// impl MyLog for U {
323/// /// Logarithm base 2 for `${U}`
324/// fn my_log2(self) -> u32 {
325/// MyLog::my_log2(*self)
326/// }
327/// }
328/// ```
329#[proc_macro_attribute]
330#[proc_macro_error]
331pub fn trait_gen(args: TokenStream, item: TokenStream) -> TokenStream {
332 macro_trait_gen(args.into(), item.into()).into()
333}
334
335#[cfg(not(feature = "no_type_gen"))]
336/// Generates the attached implementation code for all the types given in argument.
337///
338/// This is a synonym of the [trait_gen()] attribute, provided because the attribute can be used with
339/// other elements than trait implementations.
340#[proc_macro_attribute]
341#[proc_macro_error]
342pub fn type_gen(args: TokenStream, item: TokenStream) -> TokenStream {
343 macro_trait_gen(args.into(), item.into()).into()
344}
345
346//==============================================================================
347// Conditional attributes
348
349/// Generates the attached code if the condition is met.
350///
351/// Please refer to the [crate documentation](crate#conditional-code).
352#[proc_macro_attribute]
353#[proc_macro_error]
354pub fn trait_gen_if(args: TokenStream, item: TokenStream) -> TokenStream {
355 macro_trait_gen_if("trait_gen_if", args.into(), item.into()).into()
356}
357
358#[cfg(not(feature = "no_type_gen"))]
359/// Generates the attached code if the condition is met.
360///
361/// This is a synonym of the [trait_gen_if()] attribute, provided because the attribute can be used with
362/// other elements than trait implementations.
363///
364/// Please refer to the [crate documentation](crate#conditional-code).
365#[proc_macro_attribute]
366#[proc_macro_error]
367pub fn type_gen_if(args: TokenStream, item: TokenStream) -> TokenStream {
368 macro_trait_gen_if("type_gen_if", args.into(), item.into()).into()
369}