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