rustifact/
tokens.rs

1use proc_macro2::{Ident, Literal, Span, TokenStream};
2use quote::{quote, TokenStreamExt};
3
4/// Provides a flexible interface for converting Rust's data types into their token stream representation.
5/// This trait is akin to `quote::ToTokens`, with a similar design, but it serves a distinct purpose.
6///
7/// Rust's `quote` crate is a fantastic tool for metaprogramming, offering the ability to produce Rust code
8/// within Rust itself. It exposes the `ToTokens` trait, which types can implement to define how they can
9/// be turned into a token stream, i.e., a sequence of Rust's syntactic tokens.
10///
11/// However, `ToTokens` in `quote` does not provide out-of-the-box implementations for certain common
12/// data types like tuples. Furthermore, due to Rust's orphan rule, we can't implement `ToTokens` for
13/// these types outside of the `quote` crate. This limitation is unacceptable given the expected use cases of _Rustifact_.
14///
15/// # Design
16///
17/// The trait exposes three primary methods:
18///
19/// - `to_toks(&self, toks: &mut TokenStream)`: Defines how the type is converted into a token stream. This is the primary method implementers should focus on.
20///
21/// - `to_tok_stream(&self) -> TokenStream`: A helper method which leverages `to_toks` to generate a new token stream.
22///
23/// - `to_tokens(&self, toks: &mut TokenStream)`: This method mirrors `to_toks` and is included for compatibility with `quote::ToTokens`.
24///
25/// This crate also provides implementations for a range of primitive types, booleans, references, arrays, and vectors.
26///
27pub trait ToTokenStream {
28    fn to_toks(&self, toks: &mut TokenStream);
29
30    fn to_tok_stream(&self) -> TokenStream {
31        let mut tokens = TokenStream::new();
32        self.to_toks(&mut tokens);
33        tokens
34    }
35
36    fn to_tokens(&self, toks: &mut TokenStream) {
37        self.to_toks(toks);
38    }
39}
40
41macro_rules! primitive {
42    ($($t:ty => $name:ident)*) => {
43        $(
44            impl ToTokenStream for $t {
45                fn to_toks(&self, tokens: &mut TokenStream) {
46                    tokens.append(Literal::$name(*self));
47                }
48            }
49        )*
50    };
51}
52
53primitive! {
54    i8 => i8_suffixed
55    i16 => i16_suffixed
56    i32 => i32_suffixed
57    i64 => i64_suffixed
58    i128 => i128_suffixed
59    isize => isize_suffixed
60
61    u8 => u8_suffixed
62    u16 => u16_suffixed
63    u32 => u32_suffixed
64    u64 => u64_suffixed
65    u128 => u128_suffixed
66    usize => usize_suffixed
67
68    f32 => f32_suffixed
69    f64 => f64_suffixed
70
71    char => character
72    &str => string
73}
74
75impl ToTokenStream for bool {
76    fn to_toks(&self, tokens: &mut TokenStream) {
77        tokens.append(Ident::new(&self.to_string(), Span::call_site()));
78    }
79}
80
81impl<'a, T: ?Sized + ToTokenStream> ToTokenStream for &'a T {
82    fn to_toks(&self, tokens: &mut TokenStream) {
83        (**self).to_toks(tokens);
84    }
85}
86
87impl<'a, T: ?Sized + ToTokenStream> ToTokenStream for &'a mut T {
88    fn to_toks(&self, tokens: &mut TokenStream) {
89        (**self).to_toks(tokens);
90    }
91}
92
93fn to_toks_slice<T>(sl: &[T], tokens: &mut TokenStream)
94where
95    T: ToTokenStream,
96{
97    let mut arr_toks = TokenStream::new();
98    for a in sl.iter() {
99        let a_toks = a.to_tok_stream();
100        let element = quote! { #a_toks, };
101        arr_toks.extend(element);
102    }
103    let element = quote! { [#arr_toks] };
104    tokens.extend(element);
105}
106
107impl<T> ToTokenStream for &[T]
108where
109    T: ToTokenStream,
110{
111    fn to_toks(&self, tokens: &mut TokenStream) {
112        to_toks_slice(self, tokens);
113    }
114}
115
116impl<T, const N: usize> ToTokenStream for [T; N]
117where
118    T: ToTokenStream,
119{
120    fn to_toks(&self, tokens: &mut TokenStream) {
121        to_toks_slice(self, tokens);
122    }
123}
124
125impl ToTokenStream for String {
126    fn to_toks(&self, tokens: &mut TokenStream) {
127        tokens.extend(quote! { #self });
128    }
129}
130
131impl<T> ToTokenStream for Vec<T>
132where
133    T: ToTokenStream,
134{
135    fn to_toks(&self, tokens: &mut TokenStream) {
136        let mut arr_toks = TokenStream::new();
137        for a in self {
138            let a_toks = a.to_tok_stream();
139            let element = quote! { #a_toks, };
140            arr_toks.extend(element);
141        }
142        let element = quote! { vec![#arr_toks] };
143        tokens.extend(element);
144    }
145}
146
147impl<T> ToTokenStream for Option<T>
148where
149    T: ToTokenStream,
150{
151    fn to_toks(&self, tokens: &mut TokenStream) {
152        let element;
153        match self {
154            Some(a) => {
155                let a_toks = a.to_tok_stream();
156                element = quote! {
157                    Some(#a_toks)
158                };
159            }
160            None => {
161                element = quote! { None };
162            }
163        }
164        tokens.extend(element);
165    }
166}
167
168macro_rules! build_tuple_trait {
169    ($($id:ident),+;$($index:literal),+) => {
170        fn to_toks(&self, tokens: &mut TokenStream) {
171            // As of Rust 1.69, limitations in the macro system mean
172            // we can't use tuple indexing with the form self.$index,
173            // so we destructure and use shadowing instead.
174            let ($($id),+) = self;
175            $(let $id = $id.to_tok_stream();)+
176            let element = quote! { ($(#$id),+) };
177            tokens.extend(element);
178        }
179    };
180}
181
182impl<T1, T2> ToTokenStream for (T1, T2)
183where
184    T1: ToTokenStream,
185    T2: ToTokenStream,
186{
187    build_tuple_trait!(t1, t2; 0, 1);
188}
189
190impl<T1, T2, T3> ToTokenStream for (T1, T2, T3)
191where
192    T1: ToTokenStream,
193    T2: ToTokenStream,
194    T3: ToTokenStream,
195{
196    build_tuple_trait!(t1, t2, t3; 0, 1, 2);
197}
198
199impl<T1, T2, T3, T4> ToTokenStream for (T1, T2, T3, T4)
200where
201    T1: ToTokenStream,
202    T2: ToTokenStream,
203    T3: ToTokenStream,
204    T4: ToTokenStream,
205{
206    build_tuple_trait!(t1, t2, t3, t4; 0, 1, 2, 3);
207}
208
209impl<T1, T2, T3, T4, T5> ToTokenStream for (T1, T2, T3, T4, T5)
210where
211    T1: ToTokenStream,
212    T2: ToTokenStream,
213    T3: ToTokenStream,
214    T4: ToTokenStream,
215    T5: ToTokenStream,
216{
217    build_tuple_trait!(t1, t2, t3, t4, t5; 0, 1, 2, 3, 4);
218}
219
220impl<T1, T2, T3, T4, T5, T6> ToTokenStream for (T1, T2, T3, T4, T5, T6)
221where
222    T1: ToTokenStream,
223    T2: ToTokenStream,
224    T3: ToTokenStream,
225    T4: ToTokenStream,
226    T5: ToTokenStream,
227    T6: ToTokenStream,
228{
229    build_tuple_trait!(t1, t2, t3, t4, t5, t6; 0, 1, 2, 3, 4, 5);
230}
231
232impl<T1, T2, T3, T4, T5, T6, T7> ToTokenStream for (T1, T2, T3, T4, T5, T6, T7)
233where
234    T1: ToTokenStream,
235    T2: ToTokenStream,
236    T3: ToTokenStream,
237    T4: ToTokenStream,
238    T5: ToTokenStream,
239    T6: ToTokenStream,
240    T7: ToTokenStream,
241{
242    build_tuple_trait!(t1, t2, t3, t4, t5, t6, t7; 0, 1, 2, 3, 4, 5, 6);
243}
244
245impl<T1, T2, T3, T4, T5, T6, T7, T8> ToTokenStream for (T1, T2, T3, T4, T5, T6, T7, T8)
246where
247    T1: ToTokenStream,
248    T2: ToTokenStream,
249    T3: ToTokenStream,
250    T4: ToTokenStream,
251    T5: ToTokenStream,
252    T6: ToTokenStream,
253    T7: ToTokenStream,
254    T8: ToTokenStream,
255{
256    build_tuple_trait!(t1, t2, t3, t4, t5, t6, t7, t8; 0, 1, 2, 3, 4, 5, 6, 7);
257}
258
259impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> ToTokenStream for (T1, T2, T3, T4, T5, T6, T7, T8, T9)
260where
261    T1: ToTokenStream,
262    T2: ToTokenStream,
263    T3: ToTokenStream,
264    T4: ToTokenStream,
265    T5: ToTokenStream,
266    T6: ToTokenStream,
267    T7: ToTokenStream,
268    T8: ToTokenStream,
269    T9: ToTokenStream,
270{
271    build_tuple_trait!(t1, t2, t3, t4, t5, t6, t7, t8, t9; 0, 1, 2, 3, 4, 5, 6, 7, 8);
272}
273
274impl<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ToTokenStream
275    for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
276where
277    T1: ToTokenStream,
278    T2: ToTokenStream,
279    T3: ToTokenStream,
280    T4: ToTokenStream,
281    T5: ToTokenStream,
282    T6: ToTokenStream,
283    T7: ToTokenStream,
284    T8: ToTokenStream,
285    T9: ToTokenStream,
286    T10: ToTokenStream,
287{
288    build_tuple_trait!(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
289}
290
291impl<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> ToTokenStream
292    for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
293where
294    T1: ToTokenStream,
295    T2: ToTokenStream,
296    T3: ToTokenStream,
297    T4: ToTokenStream,
298    T5: ToTokenStream,
299    T6: ToTokenStream,
300    T7: ToTokenStream,
301    T8: ToTokenStream,
302    T9: ToTokenStream,
303    T10: ToTokenStream,
304    T11: ToTokenStream,
305{
306    build_tuple_trait!(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
307}
308
309impl<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> ToTokenStream
310    for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
311where
312    T1: ToTokenStream,
313    T2: ToTokenStream,
314    T3: ToTokenStream,
315    T4: ToTokenStream,
316    T5: ToTokenStream,
317    T6: ToTokenStream,
318    T7: ToTokenStream,
319    T8: ToTokenStream,
320    T9: ToTokenStream,
321    T10: ToTokenStream,
322    T11: ToTokenStream,
323    T12: ToTokenStream,
324{
325    build_tuple_trait!(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
326}