tauri_wasm/
invoke.rs

1use {
2    crate::{error::Error, ext},
3    js_sys::{ArrayBuffer, JsString, Uint8Array},
4    std::{
5        pin::Pin,
6        task::{Context, Poll},
7    },
8    wasm_bindgen::prelude::*,
9    wasm_bindgen_futures::JsFuture,
10};
11
12pub(crate) mod api {
13    use super::*;
14
15    /// Invokes a [command] on the backend.
16    ///
17    /// /// [command]: https://v2.tauri.app/develop/calling-rust/#commands
18    ///
19    /// This function returns a future-like object that
20    /// can be extended with additional properties.
21    /// See [`with_args`](Invoke::with_args) and
22    /// [`with_options`](Invoke::with_options) for details.
23    ///
24    /// # Example
25    ///
26    /// ```
27    /// # async fn e() -> Result<(), tauri_wasm::Error> {
28    /// use gloo::console;
29    ///
30    /// let message = tauri_wasm::invoke("connect").await?;
31    /// console::log!("connected to backend", message);
32    /// # Ok(())
33    /// # }
34    /// ```
35    #[inline]
36    pub fn invoke<C>(cmd: C) -> Invoke<C::Js>
37    where
38        C: ToStringValue,
39    {
40        let cmd = cmd.to_string_value();
41        let args = JsValue::UNDEFINED;
42        let opts = Options::empty();
43        Invoke { cmd, args, opts }
44    }
45}
46
47pub struct Invoke<C, A = JsValue> {
48    cmd: C,
49    args: A,
50    opts: Options,
51}
52
53impl<C, A> Invoke<C, A> {
54    /// Invokes a [command] with arguments on the backend.
55    ///
56    /// [command]: https://v2.tauri.app/develop/calling-rust/#commands
57    ///
58    /// To send a custom serializable type as arguments,
59    /// use the helper [`args`](crate::args) function.
60    ///
61    /// # Example
62    ///
63    #[cfg_attr(feature = "serde", doc = "```")]
64    #[cfg_attr(not(feature = "serde"), doc = "```ignore")]
65    /// # async fn e() -> Result<(), tauri_wasm::Error> {
66    /// use {gloo::console, serde::Serialize};
67    ///
68    /// #[derive(Serialize)]
69    /// struct User<'str> {
70    ///     name: &'str str,
71    ///     pass: &'str str,
72    /// }
73    ///
74    /// let user = User {
75    ///     name: "anon",
76    ///     pass: "p@$$w0rD",
77    /// };
78    ///
79    /// let args = tauri_wasm::args(&user)?;
80    /// let message = tauri_wasm::invoke("login").with_args(args).await?;
81    /// console::log!("logged on backend", message);
82    /// # Ok(())
83    /// # }
84    /// ```
85    #[inline]
86    pub fn with_args<T>(self, args: T) -> Invoke<C, T::JsValue>
87    where
88        T: ToArgs,
89    {
90        let cmd = self.cmd;
91        let args = args.to_args();
92        let opts = self.opts;
93        Invoke { cmd, args, opts }
94    }
95
96    /// Invokes a [command] with options on the backend.
97    ///
98    /// [command]: https://v2.tauri.app/develop/calling-rust/#commands
99    ///
100    /// # Example
101    ///
102    #[cfg_attr(feature = "serde", doc = "```")]
103    #[cfg_attr(not(feature = "serde"), doc = "```ignore")]
104    /// # async fn e() -> Result<(), tauri_wasm::Error> {
105    /// use {gloo::console, tauri_wasm::invoke::Options};
106    ///
107    /// let opts = Options::from_record([
108    ///     ("secret", "2"),
109    ///     ("data", "3"),
110    /// ])?;
111    ///
112    /// let message = tauri_wasm::invoke("send").with_options(opts).await?;
113    /// console::log!("received from backend", message);
114    /// # Ok(())
115    /// # }
116    /// ```
117    #[inline]
118    pub fn with_options(self, opts: Options) -> Self {
119        Self { opts, ..self }
120    }
121}
122
123pub struct InvokeFuture(JsFuture);
124
125impl InvokeFuture {
126    #[inline]
127    pub fn into_future(self) -> JsFuture {
128        self.0
129    }
130}
131
132impl Future for InvokeFuture {
133    type Output = Result<JsValue, Error>;
134
135    #[inline]
136    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
137        let me = self.get_mut();
138        Pin::new(&mut me.0).poll(cx).map_err(Error)
139    }
140}
141
142impl<C, A> IntoFuture for Invoke<C, A>
143where
144    C: AsRef<JsValue>,
145    A: AsRef<JsValue>,
146{
147    type Output = Result<JsValue, Error>;
148    type IntoFuture = InvokeFuture;
149
150    #[inline]
151    fn into_future(self) -> Self::IntoFuture {
152        let promise = ext::invoke(self.cmd.as_ref(), self.args.as_ref(), self.opts);
153        InvokeFuture(JsFuture::from(promise))
154    }
155}
156
157pub trait ToStringValue {
158    type Js: AsRef<JsValue>;
159    fn to_string_value(self) -> Self::Js;
160}
161
162impl ToStringValue for JsString {
163    type Js = JsValue;
164
165    #[inline]
166    fn to_string_value(self) -> Self::Js {
167        JsValue::from(self)
168    }
169}
170
171impl<'str> ToStringValue for &'str JsString {
172    type Js = &'str JsValue;
173
174    #[inline]
175    fn to_string_value(self) -> Self::Js {
176        self
177    }
178}
179
180impl ToStringValue for String {
181    type Js = JsValue;
182
183    #[inline]
184    fn to_string_value(self) -> Self::Js {
185        (&self).to_string_value()
186    }
187}
188
189impl ToStringValue for &String {
190    type Js = JsValue;
191
192    #[inline]
193    fn to_string_value(self) -> Self::Js {
194        JsValue::from(self)
195    }
196}
197
198impl ToStringValue for &str {
199    type Js = JsValue;
200
201    #[inline]
202    fn to_string_value(self) -> Self::Js {
203        JsValue::from(self)
204    }
205}
206
207pub trait ToArgs {
208    type JsValue: AsRef<JsValue>;
209    fn to_args(self) -> Self::JsValue;
210}
211
212impl ToArgs for ArrayBuffer {
213    type JsValue = JsValue;
214
215    #[inline]
216    fn to_args(self) -> Self::JsValue {
217        JsValue::from(self)
218    }
219}
220
221impl<'arr> ToArgs for &'arr ArrayBuffer {
222    type JsValue = &'arr JsValue;
223
224    #[inline]
225    fn to_args(self) -> Self::JsValue {
226        self
227    }
228}
229
230impl ToArgs for Uint8Array {
231    type JsValue = JsValue;
232
233    #[inline]
234    fn to_args(self) -> Self::JsValue {
235        JsValue::from(self)
236    }
237}
238
239impl<'arr> ToArgs for &'arr Uint8Array {
240    type JsValue = &'arr JsValue;
241
242    #[inline]
243    fn to_args(self) -> Self::JsValue {
244        self
245    }
246}
247
248impl ToArgs for &[u8] {
249    type JsValue = JsValue;
250
251    #[inline]
252    fn to_args(self) -> Self::JsValue {
253        Uint8Array::from(self).to_args()
254    }
255}
256
257impl<const N: usize> ToArgs for &[u8; N] {
258    type JsValue = JsValue;
259
260    #[inline]
261    fn to_args(self) -> Self::JsValue {
262        self.as_slice().to_args()
263    }
264}
265
266#[wasm_bindgen]
267pub struct Options {
268    pub(crate) headers: JsValue,
269}
270
271impl Options {
272    pub(crate) const fn empty() -> Self {
273        let headers = JsValue::UNDEFINED;
274        Self { headers }
275    }
276}
277
278#[wasm_bindgen]
279impl Options {
280    #[inline]
281    #[wasm_bindgen(getter)]
282    pub fn headers(self) -> JsValue {
283        self.headers
284    }
285}
286
287pub trait ToHeaders {
288    fn to_headers(self) -> Result<JsValue, JsValue>;
289
290    #[inline]
291    fn to_options(self) -> Result<Options, Error>
292    where
293        Self: Sized,
294    {
295        let headers = self.to_headers().map_err(Error)?;
296        Ok(Options { headers })
297    }
298}