leptos_workers/lib.rs
1#![warn(clippy::pedantic)]
2#![allow(clippy::manual_async_fn)]
3#![allow(clippy::type_complexity)]
4#![allow(clippy::module_name_repetitions)]
5#![warn(clippy::unwrap_used)]
6#![warn(clippy::panic)]
7#![warn(missing_docs)]
8#![allow(clippy::doc_markdown)]
9
10//! This is an abstraction layer on top of Web Workers to make them easier to work with.
11//! Web workers are useful to offload heavy computations from the main rendering thread,
12//! keeping the user interface interactive even if the computation is an intense synchronous loop.
13//!
14//! To get started, take a look at the documentation for the [worker] macro.
15//!
16//! **Note**: The crate relies on having support for
17//! [ECMAScript modules in workers](https://caniuse.com/mdn-api_worker_worker_ecmascript_modules),
18//! so keep that in mind with regards to compatibility.
19//!
20//! For support, check out the #libraries channel on the official [Leptos Discord Channel](https://discord.gg/v38Eef6sWG).
21//! Feel free to ping @Jinxit.
22
23pub mod executors;
24mod messages;
25mod plumbing;
26pub mod workers;
27
28pub use messages::*;
29
30/// The main macro powering worker functions.
31///
32/// This macro accepts the following arguments:
33///
34/// 1. **Optional**: The name of the worker, typically in PascalCase.
35/// This will not be visible if using the worker function directly, but is used internally
36/// as well as if you decide to use explicit executors.
37///
38/// Worker functions come in four flavours. Common to all are the following requirements:
39///
40/// - The Request and Response types must implement `Clone`, [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html)
41/// and [`DeserializeOwned`](https://docs.rs/serde/latest/serde/de/trait.DeserializeOwned.html).
42///
43/// The different flavours all have slightly different usage scenarios:
44///
45/// | Flavour | Request | Response | Usecase |
46/// | - | - | - | - |
47/// | **[Callback](attr.worker.html#callback)** | Single | Multiple | Integrating with synchronous code based on callbacks, for example to report progress |
48/// | **[Channel](attr.worker.html#channel)** | Multiple | Multiple | For a duplex connection to a persistent worker, rather than one-off tasks |
49/// | **[Future](attr.worker.html#future)** | Single | Single | Running a single calculation without the need for progress updates |
50/// | **[Stream](attr.worker.html#stream)** | Single | Multiple | Running an asynchronous calculation with multiple responses |
51///
52/// ## Callback
53/// See also: [`CallbackWorker`](crate::workers::CallbackWorker).
54///
55/// Define your worker function as such:
56///
57/// ```
58/// # use serde::{Serialize, Deserialize};
59/// # #[derive(Clone, Serialize, Deserialize)]
60/// # pub struct MyRequest;
61/// # #[derive(Clone, Serialize, Deserialize)]
62/// # pub struct MyResponse;
63/// # use leptos_workers::worker;
64/// #[worker(MyCallbackWorker)]
65/// /*pub?*/ /*async?*/ fn worker(
66/// req: MyRequest,
67/// callback: impl Fn(MyResponse)
68/// )
69/// # { todo!() }
70/// ```
71///
72/// Which will be transformed to:
73///
74/// ```
75/// # pub struct MyRequest;
76/// # pub struct MyResponse;
77/// /*pub?*/ async fn worker(
78/// req: MyRequest,
79/// callback: impl Fn(MyResponse) + 'static
80/// ) -> Result<(), leptos_workers::CreateWorkerError>
81/// # { todo!() }
82/// ```
83///
84/// ## Channel
85/// See also: [`ChannelWorker`](crate::workers::ChannelWorker).
86///
87/// Define your worker function as such:
88///
89/// ```
90/// # use serde::{Serialize, Deserialize};
91/// # #[derive(Clone, Serialize, Deserialize)]
92/// # pub struct MyInit;
93/// # #[derive(Clone, Serialize, Deserialize)]
94/// # pub struct MyRequest;
95/// # #[derive(Clone, Serialize, Deserialize)]
96/// # pub struct MyResponse;
97/// # use leptos_workers::worker;
98/// #[worker(MyChannelWorker)]
99/// /*pub?*/ /*async?*/ fn worker(
100/// init: MyInit, // Optional parameter: initialization input for the worker, requires Clone + Serialize + DeserializeOwned.
101/// rx: leptos_workers::Receiver<MyRequest>,
102/// tx: leptos_workers::Sender<MyResponse>
103/// )
104/// # { todo!() }
105/// ```
106///
107/// Which will be transformed to:
108///
109/// ```
110/// # pub struct MyInit;
111/// # pub struct MyRequest;
112/// # pub struct MyResponse;
113/// /*pub?*/ fn worker(init: MyInit) -> Result<
114/// (
115/// leptos_workers::Sender<MyRequest>,
116/// leptos_workers::Receiver<MyResponse>,
117/// ),
118/// leptos_workers::CreateWorkerError,
119/// >
120/// # { todo!() }
121/// ```
122///
123/// ## Future
124/// See also: [`FutureWorker`](crate::workers::FutureWorker).
125///
126/// Define your worker function as such:
127///
128/// ```
129/// # use serde::{Serialize, Deserialize};
130/// # #[derive(Clone, Serialize, Deserialize)]
131/// # pub struct MyRequest;
132/// # #[derive(Clone, Serialize, Deserialize)]
133/// # pub struct MyResponse;
134/// # use leptos_workers::worker;
135/// #[worker(MyFutureWorker)]
136/// /*pub?*/ /*async?*/ fn worker(
137/// req: MyRequest
138/// ) -> MyResponse
139/// # { todo!() }
140/// ```
141///
142/// Which will be transformed to:
143///
144/// ```
145/// # pub struct MyRequest;
146/// # pub struct MyResponse;
147/// /*pub?*/ async fn worker(
148/// req: MyRequest,
149/// ) -> Result<MyResponse, leptos_workers::CreateWorkerError>
150/// # { todo!() }
151/// ```
152///
153/// ## Stream
154/// See also: [`StreamWorker`](crate::workers::StreamWorker).
155///
156/// Define your worker function as such:
157///
158/// ```
159/// # use serde::{Serialize, Deserialize};
160/// # #[derive(Clone, Serialize, Deserialize)]
161/// # pub struct MyRequest;
162/// # #[derive(Clone, Serialize, Deserialize)]
163/// # pub struct MyResponse;
164/// # use leptos_workers::worker;
165/// #[worker(MyStreamWorker)]
166/// /*pub?*/ /*async?*/ fn worker(
167/// req: MyRequest
168/// ) -> impl leptos_workers::Stream<Item = MyResponse>
169/// # { futures::stream::once(async { MyResponse }) }
170/// ```
171///
172/// Which will be transformed to:
173///
174/// ```
175/// # pub struct MyRequest;
176/// # pub struct MyResponse;
177/// /*pub?*/ fn worker(
178/// req: &MyRequest,
179/// ) -> Result<
180/// impl leptos_workers::Stream<Item = MyResponse>,
181/// leptos_workers::CreateWorkerError
182/// >
183/// # { Ok(futures::stream::once(async { MyResponse })) }
184/// ```
185///
186/// # Usage
187///
188/// After applying the macro to one of the above examples they can be called directly. This will use a default thread pool.
189/// Don't forget to handle the error case if you need compatibility with older browsers.
190///
191/// ```
192/// # use serde::{Serialize, Deserialize};
193/// # use leptos_workers::worker;
194/// # fn create_local_resource<T>(f: fn(), g: fn(Option<T>) -> T) {}
195/// #[derive(Clone, Serialize, Deserialize)]
196/// pub struct MyRequest;
197/// #[derive(Clone, Serialize, Deserialize)]
198/// pub struct MyResponse;
199///
200/// #[worker(MyFutureWorker)]
201/// pub async fn future_worker(_req: MyRequest) -> MyResponse {
202/// MyResponse
203/// }
204/// # fn main() {
205/// // in your component
206/// let response = create_local_resource(|| (), move |_| {
207/// future_worker(MyRequest)
208/// });
209/// # }
210/// ```
211///
212/// **If using SSR it is important to ensure the worker only executes on the client**,
213/// as the worker does not exist on the server. A few examples of wrapping functions include:
214/// - `create_local_resource`
215/// - `create_effect`
216/// - `create_action`
217/// - `spawn_local`
218///
219/// If using pure CSR, this is not a problem.
220pub use leptos_workers_macro::worker;
221
222pub use plumbing::CreateWorkerError;
223
224#[doc(no_inline)]
225pub use flume::Receiver;
226#[doc(no_inline)]
227pub use flume::Sender;
228#[doc(no_inline)]
229pub use futures::stream::Stream;
230
231#[doc(hidden)]
232pub use futures::future::LocalBoxFuture;
233#[doc(hidden)]
234pub use futures::stream::LocalBoxStream;
235#[doc(hidden)]
236pub use futures::stream::StreamExt;
237#[doc(hidden)]
238pub use wasm_bindgen;
239
240extern crate alloc;
241extern crate core;