webview2_com_macros/
lib.rs1use 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#[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#[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}