thruster_proc/
lib.rs

1// #![feature(proc_macro_diagnostic)]
2// extern crate proc_macro;
3
4use proc_macro::TokenStream;
5use proc_macro2::{Ident, Span as Span2, TokenStream as TokenStream2, TokenTree as TokenTree2};
6use quote::quote;
7
8mod json;
9
10#[proc_macro_attribute]
11pub fn json_request(attr: TokenStream, item: TokenStream) -> TokenStream {
12    json::json_request(attr, item)
13}
14
15#[proc_macro]
16pub fn m(items: TokenStream) -> TokenStream {
17    let items = proc_macro2::TokenStream::from(items);
18
19    let idents = items
20        .into_iter()
21        .filter(|v| matches!(v, TokenTree2::Ident(_)))
22        .clone();
23    let pointers = idents.clone().into_iter().map(|_| {
24        quote! {
25            MiddlewareFnPointer<_>
26        }
27    });
28
29    let gen = quote! {
30        {
31            use thruster::parser::middleware_traits::{MiddlewareFnPointer, MiddlewareTuple, ToTuple};
32
33            let val: (#( #pointers),*,) = (#( #idents ),*,);
34            val.to_tuple()
35        }
36    };
37
38    // proc_macro::Span::call_site()
39    //     .note("Thruster code output")
40    //     .note(gen.to_string())
41    //     .emit();
42
43    gen.into()
44}
45
46#[deprecated(note = "Will be removed in future versions in favor of the simpler m macro")]
47#[proc_macro]
48pub fn async_middleware(items: TokenStream) -> TokenStream {
49    let items = proc_macro2::TokenStream::from(items);
50
51    let mut item_iter = items.into_iter();
52
53    item_iter.next();
54    item_iter.next();
55
56    let items = match item_iter.next() {
57        Some(TokenTree2::Group(g)) => g.stream(),
58        _ => panic!("Second item should be a group."),
59    };
60
61    let idents = items
62        .into_iter()
63        .filter(|v| matches!(v, TokenTree2::Ident(_)))
64        .clone();
65    let pointers = idents.clone().into_iter().map(|_| {
66        quote! {
67            MiddlewareFnPointer<_>
68        }
69    });
70
71    let gen = quote! {
72        {
73            use thruster::parser::middleware_traits::{MiddlewareFnPointer, MiddlewareTuple, ToTuple};
74
75            let val: (#( #pointers),*,) = (#( #idents ),*,);
76            val.to_tuple()
77        }
78    };
79
80    // proc_macro::Span::call_site()
81    //     .note("Thruster code output")
82    //     .note(gen.to_string())
83    //     .emit();
84
85    gen.into()
86}
87
88#[proc_macro_attribute]
89pub fn middleware(attr: TokenStream, item: TokenStream) -> TokenStream {
90    middleware_fn(attr, item)
91}
92
93#[proc_macro_attribute]
94pub fn middleware_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
95    if let syn::Item::Fn(mut function_item) = syn::parse(item.clone()).unwrap() {
96        let name = function_item.sig.ident.clone();
97        let new_name = Ident::new(&format!("__async_{}", name), Span2::call_site());
98        function_item.sig.ident = new_name.clone();
99
100        let visibility = function_item.vis.clone();
101        let arguments = function_item.sig.inputs.clone();
102        let generics = function_item.sig.generics.clone();
103
104        let context_type = match &arguments[0] {
105            syn::FnArg::Typed(cap) => &cap.ty,
106            _ => panic!("Expected the first argument to be a context type"),
107        };
108        let new_return_type = Ident::new(
109            &format!("__MiddlewareReturnValue_{}", name),
110            Span2::call_site(),
111        );
112        let new_rbf_type = Ident::new(&format!("__ReusableBoxFuture_{}", name), Span2::call_site());
113        let crate_path = match attr.to_string().as_str() {
114            "_internal" => quote! {
115                crate::{core::{ MiddlewareReturnValue as #new_return_type }, ReusableBoxFuture as #new_rbf_type }
116            },
117            _ => quote! {
118                thruster::{ MiddlewareReturnValue as #new_return_type, ReusableBoxFuture as #new_rbf_type }
119            },
120        };
121
122        let gen = quote! {
123            #function_item
124
125            use #crate_path;
126            #visibility fn #name#generics(ctx: #context_type, next: MiddlewareNext<#context_type>) -> #new_return_type<#context_type> {
127                #new_rbf_type::new(#new_name(ctx, next))
128            }
129        };
130
131        // proc_macro::Span::call_site()
132        //     .note("Thruster code output")
133        //     .note(gen.to_string())
134        //     .emit();
135
136        gen.into()
137    } else {
138        item
139    }
140}
141
142#[proc_macro_attribute]
143pub fn context_state(_attr: TokenStream, item: TokenStream) -> TokenStream {
144    let item = proc_macro2::TokenStream::from(item);
145    let mut items = item.clone().into_iter();
146
147    loop {
148        if let Some(TokenTree2::Ident(i)) = items.next() {
149            if &i.to_string() == "struct" {
150                break;
151            }
152        } else {
153            panic!("First token must be an identifier, like `State` or `Config`.");
154        }
155    }
156
157    let name = if let Some(TokenTree2::Ident(i)) = items.next() {
158        i
159    } else {
160        panic!("First token must be an identifier, like `State` or `Config`.");
161    };
162
163    let mut groups = vec![];
164    let mut current_group = vec![];
165
166    if let Some(TokenTree2::Group(items)) = items.next() {
167        for token in items.stream().into_iter() {
168            if let TokenTree2::Punct(p) = &token {
169                if p.as_char() == ',' {
170                    groups.push(current_group);
171                    current_group = vec![];
172                } else {
173                    current_group.push(token);
174                }
175            } else {
176                current_group.push(token);
177            }
178        }
179    } else {
180        panic!("Third argument must be a group in the form of [], (), or braces.");
181    }
182
183    if !current_group.is_empty() {
184        groups.push(current_group);
185    }
186
187    let groups = groups
188        .into_iter()
189        .map(|v| {
190            let mut stream = TokenStream2::new();
191            stream.extend(v);
192            stream
193        })
194        .collect::<Vec<TokenStream2>>();
195
196    let mut impls = vec![];
197
198    let mut i = 0;
199    for group in groups.iter() {
200        let i_token = proc_macro2::Literal::usize_unsuffixed(i);
201        impls.push(quote! {
202            impl ContextState<#group> for #name {
203                fn get(&self) -> &#group {
204                    &self.#i_token
205                }
206
207                fn get_mut(&mut self) -> &mut #group {
208                    &mut self.#i_token
209                }
210            }
211        });
212        i += 1;
213    }
214
215    let gen = quote! {
216        #item
217
218        use thruster::ContextState;
219
220        #(
221            #impls
222        )*
223    };
224
225    gen.into()
226}
227
228#[proc_macro]
229pub fn generate_tuples(items: TokenStream) -> TokenStream {
230    let items = proc_macro2::TokenStream::from(items);
231
232    let mut idents: Vec<Ident> = items
233        .into_iter()
234        .filter(|v| matches!(v, TokenTree2::Ident(_)))
235        .map(|v| {
236            if let TokenTree2::Ident(i) = v {
237                i
238            } else {
239                panic!("Should never get here.")
240            }
241        })
242        .collect();
243    let ident_count = idents.len();
244
245    let mut vec_collection: Vec<Vec<Ident>> = vec![];
246    let mut aggregator = vec![];
247
248    while !idents.is_empty() {
249        aggregator.push(idents.remove(0));
250
251        vec_collection.push(aggregator.clone());
252    }
253
254    let mut enum_variants = vec![];
255    let mut to_tuple_variants = vec![];
256    let mut from_tuple_variants = vec![];
257    for i in 0..vec_collection.len() {
258        let idents = vec_collection.get(i).unwrap();
259        let last_a = idents.last().unwrap();
260        let last_b = idents.last().unwrap();
261        let last_d = idents.last().unwrap();
262
263        let values_a: Vec<TokenStream2> = idents
264            .iter()
265            .map(|_v| {
266                quote! {
267                    M<T>
268                }
269            })
270            .collect();
271        let values_b: Vec<Ident> = idents
272            .iter()
273            .map(|v| Ident::new(&format!("{}", v).to_lowercase(), Span2::call_site()))
274            .collect();
275        let values_c = values_b.clone();
276        let values_e = values_a.clone();
277        let values_f = values_b.clone();
278        let values_g = values_b.clone();
279
280        enum_variants.push(quote! {
281            #last_a(#(#values_a),*)
282        });
283
284        to_tuple_variants.push(quote! {
285            MiddlewareTuple::#last_b(#(#values_b),*) => (#(#values_c),*,)
286        });
287
288        from_tuple_variants.push(quote! {
289            #[allow(unused_parens)]
290            impl<T: 'static + Send> ToTuple<T> for (#(#values_e),*,) {
291                fn to_tuple(self) -> MiddlewareTuple<T> {
292                    #[allow(non_snake_case)]
293                    let (#(#values_f),*,) = self;
294
295                    MiddlewareTuple::#last_d(#(#values_g),*)
296                }
297            }
298        });
299    }
300
301    const VALUES: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
302    let mut combine_outter = vec![];
303    for i in 0..vec_collection.len() {
304        let idents = vec_collection.get(i).unwrap();
305        let last = idents.last().unwrap();
306
307        let values_a: Vec<Ident> = idents
308            .iter()
309            .map(|v| Ident::new(&format!("{}", v).to_lowercase(), Span2::call_site()))
310            .collect();
311
312        let mut inner_values = vec![];
313        for j in 0..vec_collection.len() {
314            let inner_idents = vec_collection.get(j).unwrap();
315
316            let outter = Ident::new(&format!("{}", last).to_lowercase(), Span2::call_site());
317            let last = inner_idents.last().unwrap();
318
319            let values: Vec<Ident> = inner_idents
320                .iter()
321                .map(|v| {
322                    Ident::new(
323                        &format!("{}_{}", outter, v).to_lowercase(),
324                        Span2::call_site(),
325                    )
326                })
327                .collect();
328
329            let count_usize = i + j + 2;
330            let count = proc_macro2::Literal::usize_suffixed(count_usize);
331            if count_usize <= ident_count {
332                let output_variant = Ident::new(
333                    &format!("{}", VALUES.chars().nth(count_usize - 1).unwrap()),
334                    Span2::call_site(),
335                );
336                let values_c = values_a.clone();
337                let values_d = values.clone();
338                let values_e = values.clone();
339
340                inner_values.push(quote! {
341                    MiddlewareTuple::#last(#(#values_d),*) => MiddlewareTuple::#output_variant(#(#values_c),*, #(#values_e),*)
342                });
343            } else {
344                inner_values.push(quote! {
345                    MiddlewareTuple::#last(#(#values),*) => panic!("Can't handle {}-tuple", #count)
346                });
347            }
348        }
349
350        combine_outter.push(quote! {
351            MiddlewareTuple::#last(#(#values_a),*) => {
352                match other {
353                    #(#inner_values),*
354                }
355            }
356        });
357    }
358
359    let gen = quote! {
360        #[derive(Clone, Debug)]
361        pub enum MiddlewareTuple<T> {
362            #(
363                #enum_variants
364            ),*
365        }
366
367        pub trait ToTuple<T> {
368            fn to_tuple(self) -> MiddlewareTuple<T>;
369        }
370
371        impl<T: Send> MiddlewareTuple<T> {
372            pub fn combine(self, other: MiddlewareTuple<T>) -> MiddlewareTuple<T> {
373                match self {
374                    #(
375                        #combine_outter
376                    ),*
377                }
378            }
379        }
380
381        impl<T: 'static + Send> IntoMiddleware<T, M<T>> for MiddlewareTuple<T> {
382            fn middleware(self) -> Box<dyn Fn(T) -> ReusableBoxFuture<Result<T, ThrusterError<T>>> + Send + Sync> {
383                match self {
384                    #(
385                        #to_tuple_variants.middleware()
386                    ),*
387                }
388            }
389        }
390
391
392        #(#from_tuple_variants)*
393    };
394
395    // proc_macro::Span::call_site()
396    //     .note("Thruster code output")
397    //     .note(gen.to_string())
398    //     .emit();
399
400    gen.into()
401}