wasmworker_proc_macro/lib.rs
1#![deny(rustdoc::broken_intra_doc_links, missing_docs)]
2
3//! Procedural macros for exporting functions to WebWorkers.
4//!
5//! This crate provides two macros:
6//! - [`webworker_fn`]: For simple, synchronous functions
7//! - [`webworker_channel_fn`]: For async functions with bidirectional channel support
8
9use proc_macro::TokenStream;
10use quote::{format_ident, quote};
11use syn::{parse_macro_input, ItemFn};
12
13/// A procedural macro that exports a simple function for use with a WebWorker.
14///
15/// Use this for functions that take a single argument and return a result synchronously.
16/// The function will be callable via `WebWorkerFn` and the `webworker!` macro.
17///
18/// # Example
19///
20/// ```ignore
21/// use wasmworker_proc_macro::webworker_fn;
22///
23/// #[webworker_fn]
24/// fn sort_vec(mut v: Vec<u32>) -> Vec<u32> {
25/// v.sort();
26/// v
27/// }
28/// ```
29#[proc_macro_attribute]
30pub fn webworker_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
31 let input = parse_macro_input!(item as ItemFn);
32 let fn_name = &input.sig.ident;
33 let wrapper_fn_name = format_ident!("__webworker_{}", fn_name);
34
35 let mod_code = quote! {
36 #[doc(hidden)]
37 pub mod #fn_name {
38 pub const __WEBWORKER: () = ();
39 const _: () = {
40 #[wasm_bindgen::prelude::wasm_bindgen]
41 pub fn #wrapper_fn_name(arg: Box<[u8]>) -> Box<[u8]> {
42 let arg = wasmworker::convert::from_bytes(&arg);
43 let res = super::#fn_name(arg);
44 wasmworker::convert::to_bytes(&res)
45 }
46 };
47 }
48 };
49
50 let expanded = quote! {
51 #input
52
53 #mod_code
54 };
55
56 TokenStream::from(expanded)
57}
58
59/// A procedural macro that exports an async function with channel support for use with a WebWorker.
60///
61/// Use this for functions that need bidirectional communication with the main thread,
62/// such as progress reporting or interactive workflows. The function must be async and
63/// take a `Channel` as its second parameter.
64///
65/// The function will be callable via `WebWorkerChannelFn` and the `webworker_channel!` macro.
66///
67/// # Example
68///
69/// ```ignore
70/// use wasmworker_proc_macro::webworker_channel_fn;
71/// use wasmworker::Channel;
72///
73/// #[webworker_channel_fn]
74/// async fn process_with_progress(data: Vec<u8>, channel: Channel) -> Result<Output, Error> {
75/// channel.send(&Progress { percent: 50 });
76/// let response: UserChoice = channel.recv().await?;
77/// // ... process data ...
78/// Ok(output)
79/// }
80/// ```
81#[proc_macro_attribute]
82pub fn webworker_channel_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
83 let input = parse_macro_input!(item as ItemFn);
84 let fn_name = &input.sig.ident;
85 let wrapper_fn_name = format_ident!("__webworker_channel_{}", fn_name);
86
87 let mod_code = quote! {
88 #[doc(hidden)]
89 pub mod #fn_name {
90 pub const __WEBWORKER_CHANNEL: () = ();
91 const _: () = {
92 #[wasm_bindgen::prelude::wasm_bindgen]
93 pub async fn #wrapper_fn_name(arg: Box<[u8]>, port: wasm_bindgen::JsValue) -> Box<[u8]> {
94 use wasm_bindgen::JsCast;
95 let arg = wasmworker::convert::from_bytes(&arg);
96 let channel = port
97 .dyn_into::<wasmworker::MessagePort>()
98 .map(wasmworker::Channel::from)
99 .expect("webworker_channel_fn requires a MessagePort");
100 let res = super::#fn_name(arg, channel).await;
101 wasmworker::convert::to_bytes(&res)
102 }
103 };
104 }
105 };
106
107 let expanded = quote! {
108 #input
109
110 #mod_code
111 };
112
113 TokenStream::from(expanded)
114}