webview2_com_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{
4    parenthesized,
5    parse::{Parse, ParseStream},
6    parse_macro_input, Ident, Result, Token, TypePath, Visibility,
7};
8
9struct CallbackTypes {
10    pub interface: TypePath,
11    pub arg_1: TypePath,
12    pub arg_2: Option<TypePath>,
13}
14
15impl Parse for CallbackTypes {
16    fn parse(input: ParseStream) -> Result<Self> {
17        let content;
18        parenthesized!(content in input);
19        let args = content.parse_terminated(TypePath::parse, Token![,])?;
20        input.parse::<Token![;]>()?;
21        let mut args = args.into_iter();
22        if let (Some(interface), Some(arg_1), arg_2) = (args.next(), args.next(), args.next()) {
23            Ok(CallbackTypes {
24                interface,
25                arg_1,
26                arg_2,
27            })
28        } else {
29            Err(content.error("expected (interface, arg_1, arg_2)"))
30        }
31    }
32}
33
34struct CallbackStruct {
35    pub vis: Visibility,
36    _struct_token: Token![struct],
37    pub ident: Ident,
38    pub args: CallbackTypes,
39}
40
41impl Parse for CallbackStruct {
42    fn parse(input: ParseStream) -> Result<Self> {
43        Ok(CallbackStruct {
44            vis: input.parse()?,
45            _struct_token: input.parse()?,
46            ident: input.parse()?,
47            args: input.parse()?,
48        })
49    }
50}
51
52/// Implement a `CompletedCallback` using the types specified as tuple struct fields.
53#[proc_macro_attribute]
54pub fn completed_callback(_attr: TokenStream, input: TokenStream) -> TokenStream {
55    let ast = parse_macro_input!(input as CallbackStruct);
56    impl_completed_callback(&ast)
57}
58
59fn impl_completed_callback(ast: &CallbackStruct) -> TokenStream {
60    let vis = &ast.vis;
61
62    let name = &ast.ident;
63    let name_impl = format_ident!("{name}_Impl");
64    let closure = get_closure(name);
65    let interface = &ast.args.interface;
66    let interface = interface
67        .path
68        .segments
69        .last()
70        .expect("completed_callback should always specify an interface");
71    let interface_impl = format_ident!("{}_Impl", interface.ident);
72
73    let arg_1 = &ast.args.arg_1;
74    let arg_2 = &ast.args.arg_2;
75
76    let msg = format!("Implementation of [`{interface_impl}`].");
77
78    let gen = match arg_2 {
79        Some(arg_2) => quote! {
80            type #closure = CompletedClosure<#arg_1, #arg_2>;
81
82            #[doc = #msg]
83            #[implement(#interface)]
84            #vis struct #name(::std::cell::UnsafeCell<Option<#closure>>);
85
86            impl #name {
87                pub fn create(
88                    closure: #closure,
89                ) -> #interface {
90                    Self(Some(closure).into()).into()
91                }
92
93                pub fn wait_for_async_operation(
94                    closure: Box<
95                        dyn FnOnce(#interface) -> crate::Result<()>,
96                    >,
97                    completed: #closure,
98                ) -> crate::Result<()> {
99                    let (tx, rx) = ::std::sync::mpsc::channel();
100                    let completed: #closure =
101                        Box::new(move |arg_1, arg_2| -> ::windows::core::Result<()> {
102                            let result = completed(arg_1, arg_2).map_err(crate::Error::WindowsError);
103                            tx.send(result).expect("send over mpsc channel");
104                            Ok(())
105                        });
106                    let callback = Self::create(completed);
107
108                    closure(callback)?;
109                    crate::wait_with_pump(rx)?
110                }
111            }
112
113            #[allow(non_snake_case)]
114            impl #interface_impl for #name_impl {
115                fn Invoke<'a>(
116                    &self,
117                    arg_1: <#arg_1 as InvokeArg<'a>>::Input,
118                    arg_2: <#arg_2 as InvokeArg<'a>>::Input,
119                ) -> ::windows::core::Result<()> {
120                    match unsafe { (*self.0.get()).take() } {
121                        Some(completed) => completed(
122                            <#arg_1 as InvokeArg<'a>>::convert(arg_1),
123                            <#arg_2 as InvokeArg<'a>>::convert(arg_2),
124                        ),
125                        None => Ok(()),
126                    }
127                }
128            }
129        },
130        None => quote! {
131            type #closure = Box<dyn FnOnce(<#arg_1 as ClosureArg>::Output) -> ::windows::core::Result<()>>;
132
133            #[doc = #msg]
134            #[implement(#interface)]
135            #vis struct #name(::std::cell::UnsafeCell<Option<#closure>>);
136
137            impl #name {
138                pub fn create(
139                    closure: #closure,
140                ) -> #interface {
141                    Self(Some(closure).into()).into()
142                }
143
144                pub fn wait_for_async_operation(
145                    closure: Box<
146                        dyn FnOnce(#interface) -> crate::Result<()>,
147                    >,
148                    completed: #closure,
149                ) -> crate::Result<()> {
150                    let (tx, rx) = ::std::sync::mpsc::channel();
151                    let completed: #closure =
152                        Box::new(move |arg_1| -> ::windows::core::Result<()> {
153                            let result = completed(arg_1).map_err(crate::Error::WindowsError);
154                            tx.send(result).expect("send over mpsc channel");
155                            Ok(())
156                        });
157                    let callback = Self::create(completed);
158
159                    closure(callback)?;
160                    crate::wait_with_pump(rx)?
161                }
162            }
163
164            #[allow(non_snake_case)]
165            impl #interface_impl for #name_impl {
166                fn Invoke<'a>(
167                    &self,
168                    arg_1: <#arg_1 as InvokeArg<'a>>::Input,
169                ) -> ::windows::core::Result<()> {
170                    match unsafe { (*self.0.get()).take() } {
171                        Some(completed) => completed(
172                            <#arg_1 as InvokeArg<'a>>::convert(arg_1),
173                        ),
174                        None => Ok(()),
175                    }
176                }
177            }
178        },
179    };
180
181    gen.into()
182}
183
184/// Implement an `EventCallback` using the types specified as tuple struct fields.
185#[proc_macro_attribute]
186pub fn event_callback(_attr: TokenStream, input: TokenStream) -> TokenStream {
187    let ast = parse_macro_input!(input as CallbackStruct);
188    impl_event_callback(&ast)
189}
190
191fn impl_event_callback(ast: &CallbackStruct) -> TokenStream {
192    let vis = &ast.vis;
193
194    let name = &ast.ident;
195    let name_impl = format_ident!("{name}_Impl");
196    let closure = get_closure(name);
197
198    let interface = &ast.args.interface;
199    let interface = interface
200        .path
201        .segments
202        .last()
203        .expect("event_callback should always specify an interface");
204    let interface_impl = format_ident!("{}_Impl", interface.ident);
205
206    let arg_1 = &ast.args.arg_1;
207    let arg_2 = &ast
208        .args
209        .arg_2
210        .as_ref()
211        .expect("event_callback should always have 2 arguments");
212
213    let msg = format!("Implementation of [`{interface_impl}`].");
214
215    let gen = quote! {
216        type #closure = EventClosure<#arg_1, #arg_2>;
217
218        #[doc = #msg]
219        #[implement(#interface)]
220        #vis struct #name(::std::cell::UnsafeCell<#closure>);
221
222        impl #name {
223            pub fn create(
224                closure: #closure,
225            ) -> #interface {
226                Self(closure.into()).into()
227            }
228        }
229
230        #[allow(non_snake_case)]
231        impl #interface_impl for  #name_impl {
232            fn Invoke<'a>(
233                &self,
234                arg_1: <#arg_1 as InvokeArg<'a>>::Input,
235                arg_2: <#arg_2 as InvokeArg<'a>>::Input,
236            ) -> ::windows::core::Result<()> {
237                unsafe { (*self.0.get())(
238                    <#arg_1 as InvokeArg<'a>>::convert(arg_1),
239                    <#arg_2 as InvokeArg<'a>>::convert(arg_2),
240                ) }
241            }
242        }
243    };
244
245    gen.into()
246}
247
248fn get_closure(name: &Ident) -> Ident {
249    format_ident!("{name}Closure")
250}