tauri_interop/command/
bindings.rs

1use js_sys::{JsString, RegExp};
2use serde::de::DeserializeOwned;
3use wasm_bindgen::prelude::*;
4
5#[wasm_bindgen]
6extern "C" {
7    /// Binding for tauri's global invoke function
8    ///
9    /// - [Tauri Commands](https://v2.tauri.app/develop/calling-rust/)
10    #[wasm_bindgen(catch, js_namespace = ["window", "__TAURI__", "core"])]
11    pub async fn invoke(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>;
12
13    /// The binding for the frontend that listens to events
14    ///
15    /// [Events](https://v2.tauri.app/develop/calling-frontend/)
16    #[cfg(feature = "event")]
17    #[doc(cfg(feature = "event"))]
18    // for some reason this doc comment is seen as unused...
19    #[allow(unused_doc_comments)]
20    #[wasm_bindgen(catch, js_namespace = ["window", "__TAURI__", "event"])]
21    pub async fn listen(
22        event: &str,
23        closure: &Closure<dyn Fn(JsValue)>,
24    ) -> Result<JsValue, JsValue>;
25}
26
27enum InvokeResult {
28    Ok(JsValue),
29    Err(JsValue),
30    NotRegistered,
31}
32
33/// Wrapper for [invoke], to handle an unregistered function
34async fn wrapped_invoke(command: &str, args: JsValue) -> InvokeResult {
35    match invoke(command, args).await {
36        Ok(value) => InvokeResult::Ok(value),
37        Err(value) => {
38            if let Some(string) = value.dyn_ref::<JsString>() {
39                let regex = RegExp::new("command (\\w+) not found", "g");
40                if string.match_(&regex).is_some() {
41                    log::error!("Error: {string}");
42                    return InvokeResult::NotRegistered;
43                }
44            }
45
46            InvokeResult::Err(value)
47        }
48    }
49}
50
51/// Wrapper for [wait_invoke], to send a command without waiting for it
52pub fn fire_and_forget_invoke(command: &'static str, args: JsValue) {
53    wasm_bindgen_futures::spawn_local(wait_invoke(command, args))
54}
55
56/// Wrapper for [invoke], to await a command execution without handling the returned values
57pub async fn wait_invoke(command: &'static str, args: JsValue) {
58    wrapped_invoke(command, args).await;
59}
60
61/// Wrapper for [invoke], to return an expected [DeserializeOwned] item
62pub async fn return_invoke<T>(command: &str, args: JsValue) -> T
63where
64    T: Default + DeserializeOwned,
65{
66    match wrapped_invoke(command, args).await {
67        InvokeResult::Ok(value) => serde_wasm_bindgen::from_value(value).unwrap_or_else(|why| {
68            log::error!("Conversion failed: {why}");
69            Default::default()
70        }),
71        _ => Default::default(),
72    }
73}
74
75/// Wrapper for [invoke], to return an expected [Result<T, E>]
76pub async fn catch_invoke<T, E>(command: &str, args: JsValue) -> Result<T, E>
77where
78    T: Default + DeserializeOwned,
79    E: DeserializeOwned,
80{
81    match wrapped_invoke(command, args).await {
82        InvokeResult::Ok(value) => {
83            Ok(serde_wasm_bindgen::from_value(value).unwrap_or_else(|why| {
84                log::error!("Conversion failed: {why}");
85                Default::default()
86            }))
87        }
88        InvokeResult::Err(value) => Err(serde_wasm_bindgen::from_value(value).unwrap()),
89        InvokeResult::NotRegistered => Ok(Default::default()),
90    }
91}