tauri_plugin_async_wrapper/lib.rs
1extern crate proc_macro;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, ItemFn};
5
6/// A procedural macro that wraps a function with an asynchronous runtime using `spawn_blocking`.
7/// This macro converts a synchronous function into an asynchronous one and registers it as a Tauri command.
8///
9/// # Example
10/// ```rust
11/// #[async_wrapper]
12/// pub fn example_function(x: i32) -> Result<String, String> {
13/// Ok(format!("Result: {}", x))
14/// }
15/// ```
16/// After applying the macro, the function will be accessible as an asynchronous Tauri command.
17#[proc_macro_attribute]
18pub fn async_wrapper(_attr: TokenStream, item: TokenStream) -> TokenStream {
19 // Parse the input function definition
20 let input = parse_macro_input!(item as ItemFn);
21 // Generate the asynchronous wrapper for the input function
22 generate_async_wrapper(&input).into()
23}
24
25/// Generates an asynchronous wrapper for a given synchronous function.
26/// The wrapped function runs on a separate thread to avoid blocking the main thread.
27///
28/// # Arguments
29/// * `input` - A reference to the parsed function definition.
30///
31/// # Returns
32/// A `proc_macro2::TokenStream` containing the generated asynchronous function.
33fn generate_async_wrapper(input: &ItemFn) -> proc_macro2::TokenStream {
34 let fn_name = &input.sig.ident; // Function name
35 let fn_inputs = &input.sig.inputs; // Function arguments
36 let fn_output = &input.sig.output; // Function return type
37 let fn_block = &input.block; // Function body
38
39 // Generate the asynchronous wrapper code
40 quote! {
41 #[tauri::command]
42 pub async fn #fn_name(#fn_inputs) #fn_output {
43 tauri::async_runtime::spawn_blocking(move || {
44 #fn_block
45 })
46 .await
47 .map_err(|e| e.to_string())?
48 }
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use proc_macro2::TokenStream as TokenStream2;
56 use quote::quote;
57 use syn::parse_quote;
58
59 /// Tests the `async_wrapper` macro by validating the generated asynchronous wrapper function.
60 #[test]
61 fn test_async_wrapper() {
62 // Simulate a user-defined input function
63 let input_fn: ItemFn = parse_quote! {
64 pub fn test_function(x: i32, y: String) -> Result<String, String> {
65 Ok(format!("{} {}", x, y))
66 }
67 };
68
69 // Generate the asynchronous wrapper
70 let output = generate_async_wrapper(&input_fn);
71
72 // Expected output for the asynchronous wrapper
73 let expected_output: TokenStream2 = quote! {
74 #[tauri::command]
75 pub async fn test_function(x: i32, y: String) -> Result<String, String> {
76 tauri::async_runtime::spawn_blocking(move || {
77 { Ok(format!("{} {}", x, y)) }
78 })
79 .await
80 .map_err(|e| e.to_string())?
81 }
82 };
83
84 // Verify that the generated code matches the expected output
85 assert_eq!(output.to_string(), expected_output.to_string());
86 }
87}