tauri_async_handler/
lib.rs

1//! ## Usage
2//!
3//! Cargo.toml:
4//!
5//! ```toml
6//! [dependencies]
7//! tauri-async-handler = "0.4"
8//! ```
9//!
10//! src-tauri/main.rs:
11//!
12//! ```rust
13//! mod cmd;
14//!
15//!
16//! use serde_json::json;
17//! use tauri_async_handler::*;
18//!
19//! fn main() {
20//!   tauri::AppBuilder::new()
21//!     .async_handler(None, |cmd: cmd::Cmd| async {
22//!       use cmd::Cmd::*;
23//!       Ok(match cmd {
24//!         MyCustomCommand{ argument } => {
25//!           println!("arg {}", argument);
26//!           let world = "world";
27//!           json!({
28//!             "hello": world
29//!           })
30//!         }
31//!       })
32//!     })
33//!     .build()
34//!     .run();
35//! }
36//!
37//! ```
38//!
39//! JavaScript:
40//!
41//! ```javascript
42//! const myCustomCommand = (argument) => {
43//!   return window.tauri.promisified({
44//!     cmd: 'myCustomCommand',
45//!     argument,
46//!   })
47//! }
48//! myCustomCommand.then((r) => console.log('myCustomCommand', r))
49//! ```
50
51use async_std::task::spawn;
52use futures_channel::mpsc;
53use futures_util::stream::StreamExt;
54use serde_derive::Deserialize;
55use serde_json::Value;
56use tauri::AppBuilder;
57use tauri::{Result, WebviewMut};
58
59fn map_err<E: std::error::Error>(e: E) -> String {
60    e.to_string()
61}
62
63#[derive(Deserialize)]
64#[serde(rename_all = "camelCase")]
65struct CallbackCmd<T> {
66    #[serde(flatten)]
67    cmd: T,
68    callback: String,
69    error: String,
70}
71
72struct Command<T>(T, WebviewMut);
73
74pub trait AppBuilderExt {
75    fn async_handler<C, F, Fut>(self, limit: impl Into<Option<usize>>, invoke_handler: F) -> Self
76    where
77        C: serde::de::DeserializeOwned + Send + 'static,
78        F: FnMut(C) -> Fut + Send + 'static,
79        Fut: std::future::Future<Output = Result<Value>> + Send;
80}
81
82fn json_string(value: Value) -> String {
83    serde_json::to_string(&value).expect("Failed to encode json")
84}
85
86fn execute_callback(
87    mut handle: WebviewMut,
88    result: Result<Value>,
89    callback: String,
90    error: String,
91) {
92    handle
93        .dispatch(|mut webview| {
94            tauri::execute_promise_sync(&mut webview, || result.map(json_string), callback, error)
95                .expect("Failed to execute promise");
96        })
97        .expect("Failed to dispatch");
98}
99
100impl AppBuilderExt for AppBuilder {
101    fn async_handler<C, F, Fut>(
102        self,
103        limit: impl Into<Option<usize>>,
104        mut invoke_handler: F,
105    ) -> Self
106    where
107        C: serde::de::DeserializeOwned + Send + 'static,
108        F: FnMut(C) -> Fut + Send + 'static,
109        Fut: std::future::Future<Output = Result<Value>> + Send,
110    {
111        let limit = limit.into();
112        let (mut tx, rx) = mpsc::channel::<Command<CallbackCmd<C>>>(10);
113
114        spawn(async move {
115            rx.for_each_concurrent(limit, move |command| {
116                let Command(
117                    CallbackCmd {
118                        cmd,
119                        callback,
120                        error,
121                    },
122                    handle,
123                ) = command;
124                let fut = invoke_handler(cmd);
125                async {
126                    execute_callback(handle, fut.await, callback, error);
127                }
128            })
129            .await
130        });
131        self.invoke_handler(move |webview, arg| {
132            let handle = webview.as_mut();
133            let command: CallbackCmd<C> = serde_json::from_str(arg).map_err(map_err)?;
134            if let Err(e) = tx.try_send(Command(command, handle.clone())) {
135                let command = e.into_inner();
136                execute_callback(
137                    handle,
138                    Err(anyhow::anyhow!("Failed to execute command")),
139                    command.0.callback,
140                    command.0.error,
141                );
142            }
143            Ok(())
144        })
145    }
146}