tauri_async_handler/
lib.rs1use 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}