Skip to main content

aeth_window_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::quote;
4use syn::{FnArg, Ident, ItemFn, Pat, parse_macro_input};
5
6fn collect_fn_args(item_fn: &ItemFn) -> Vec<Ident> {
7    item_fn
8        .sig
9        .inputs
10        .iter()
11        .filter_map(|input| {
12            if let FnArg::Typed(typed) = input {
13                Some(typed)
14            } else {
15                None
16            }
17        })
18        .filter_map(|typed| {
19            if let Pat::Ident(pat) = typed.pat.as_ref() {
20                Some(pat.ident.clone())
21            } else {
22                None
23            }
24        })
25        .collect()
26}
27
28fn is_mutable_method(item_fn: &ItemFn) -> Option<bool> {
29    Some(
30        item_fn
31            .sig
32            .inputs
33            .iter()
34            .filter_map(|input| {
35                if let FnArg::Receiver(receiver) = input {
36                    Some(receiver)
37                } else {
38                    None
39                }
40            })
41            .next()?
42            .mutability
43            .is_some(),
44    )
45}
46
47#[proc_macro_attribute]
48pub fn forward_winit_window_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
49    let item_fn = parse_macro_input!(item as ItemFn);
50    let args = collect_fn_args(&item_fn);
51    let mutable =
52        is_mutable_method(&item_fn).expect("Must be a method of AccessWinitWindow trait extension");
53    let vis = item_fn.vis;
54    let sig = item_fn.sig;
55    let name = sig.ident.clone();
56    let method = Ident::new(
57        if mutable {
58            "map_winit_window_mut"
59        } else {
60            "map_winit_window"
61        },
62        Span::call_site(),
63    );
64    TokenStream::from(quote! {
65        #[doc = concat!("Invoke [`winit::window::Window::", stringify!(#name), "`].")]
66        #vis #sig {
67            self.#method(move |v| v.#name(#(#args),*))
68        }
69    })
70}
71
72#[proc_macro_attribute]
73pub fn forward_winit_active_event_loop_method(
74    _attr: TokenStream,
75    item: TokenStream,
76) -> TokenStream {
77    let item_fn = parse_macro_input!(item as ItemFn);
78    let args = collect_fn_args(&item_fn);
79    is_mutable_method(&item_fn)
80        .expect("Must be a method of AccessWinitActiveEventLoop trait extension");
81    let vis = item_fn.vis;
82    let sig = item_fn.sig;
83    let name = sig.ident.clone();
84    TokenStream::from(quote! {
85        #[doc = concat!("Invoke [`winit::event_loop::ActiveEventLoop::", stringify!(#name), "`].")]
86        #vis #sig {
87            self.run_with_active_event_loop(move |v| { v.#name(#(#args),*) })
88                .await
89        }
90    })
91}