Skip to main content

tauri/ipc/
command.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! The Tauri custom commands types and traits.
6//!
7//! You usually don't need to create these items yourself. These are created from [command](../attr.command.html)
8//! attribute macro along the way and used by [`crate::generate_handler`] macro.
9
10use crate::{
11  Runtime,
12  ipc::{InvokeBody, InvokeError, InvokeMessage},
13};
14use serde::{
15  Deserialize, Deserializer,
16  de::{Error, Visitor},
17};
18
19use tauri_utils::acl::resolved::ResolvedCommand;
20
21/// Represents a custom command.
22pub struct CommandItem<'a, R: Runtime> {
23  /// Name of the plugin if this command targets one.
24  pub plugin: Option<&'static str>,
25
26  /// The name of the command, e.g. `handler` on `#[command] fn handler(value: u64)`
27  pub name: &'static str,
28
29  /// The key of the command item, e.g. `value` on `#[command] fn handler(value: u64)`
30  pub key: &'static str,
31
32  /// The [`InvokeMessage`] that was passed to this command.
33  pub message: &'a InvokeMessage<R>,
34
35  /// The resolved ACL for this command.
36  pub acl: &'a Option<Vec<ResolvedCommand>>,
37}
38
39/// Trait implemented by command arguments to derive a value from a [`CommandItem`].
40///
41/// # Command Arguments
42///
43/// A command argument is any type that represents an item parsable from a [`CommandItem`]. Most
44/// implementations will use the data stored in [`InvokeMessage`] since [`CommandItem`] is mostly a
45/// wrapper around it.
46///
47/// # Provided Implementations
48///
49/// Tauri implements [`CommandArg`] automatically for a number of types.
50/// * [`crate::AppHandle`]
51/// * [`crate::Window`]
52/// * [`crate::Webview`]
53/// * [`crate::WebviewWindow`]
54/// * [`crate::State`]
55/// * [`crate::ipc::Channel`]
56/// * [`crate::ipc::Request`]
57///   * Gives access to the raw request body and headers instead of a deserialized argument.
58/// * [`crate::ipc::CommandScope`] and [`crate::ipc::GlobalScope`]
59///   * The scope defined by the capabilities that allowed the command, see the
60///     [scope documentation](https://v2.tauri.app/security/scope/).
61/// * `T where T: serde::Deserialize`
62///   * Any type that implements `Deserialize` can automatically be used as a [`CommandArg`].
63///
64/// Note that the types above are resolved from the command message and not from the command
65/// arguments sent by the frontend, so they do not need to be provided on the JavaScript side.
66pub trait CommandArg<'de, R: Runtime>: Sized {
67  /// Derives an instance of `Self` from the [`CommandItem`].
68  ///
69  /// If the derivation fails, the corresponding message will be rejected using [`InvokeMessage#reject`].
70  fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError>;
71}
72
73/// Automatically implement [`CommandArg`] for any type that can be deserialized.
74impl<'de, D: Deserialize<'de>, R: Runtime> CommandArg<'de, R> for D {
75  fn from_command(command: CommandItem<'de, R>) -> Result<D, InvokeError> {
76    let name = command.name;
77    let arg = command.key;
78    #[cfg(feature = "tracing")]
79    let _span = tracing::trace_span!("ipc::request::deserialize_arg", arg = arg).entered();
80    Self::deserialize(command).map_err(|e| crate::Error::InvalidArgs(name, arg, e).into())
81  }
82}
83
84/// Pass the result of [`serde_json::Value::get`] into [`serde_json::Value`]'s deserializer.
85///
86/// Returns an error if the [`CommandItem`]'s key does not exist in the value.
87macro_rules! pass {
88  ($fn:ident, $($arg:ident: $argt:ty),+) => {
89    fn $fn<V: Visitor<'de>>(self, $($arg: $argt),*) -> Result<V::Value, Self::Error> {
90      self.deserialize_json()?.$fn($($arg),*)
91    }
92  }
93}
94
95impl<'a, R: Runtime> CommandItem<'a, R> {
96  fn deserialize_json(self) -> serde_json::Result<&'a serde_json::Value> {
97    if self.key.is_empty() {
98      return Err(serde_json::Error::custom(format!(
99        "command {} has an argument with no name with a non-optional value",
100        self.name
101      )));
102    }
103
104    match &self.message.payload {
105      InvokeBody::Raw(_body) => Err(serde_json::Error::custom(format!(
106        "command {} expected a value for key {} but the IPC call used a bytes payload",
107        self.name, self.key
108      ))),
109      InvokeBody::Json(v) => match v.get(self.key) {
110        Some(value) => Ok(value),
111        None => Err(serde_json::Error::custom(format!(
112          "command {} missing required key {}",
113          self.name, self.key
114        ))),
115      },
116    }
117  }
118}
119
120/// A [`Deserializer`] wrapper around [`CommandItem`].
121///
122/// If the key doesn't exist, an error will be returned if the deserialized type is not expecting
123/// an optional item. If the key does exist, the value will be called with
124/// [`Value`](serde_json::Value)'s [`Deserializer`] implementation.
125impl<'de, R: Runtime> Deserializer<'de> for CommandItem<'de, R> {
126  type Error = serde_json::Error;
127
128  pass!(deserialize_any, visitor: V);
129  pass!(deserialize_bool, visitor: V);
130  pass!(deserialize_i8, visitor: V);
131  pass!(deserialize_i16, visitor: V);
132  pass!(deserialize_i32, visitor: V);
133  pass!(deserialize_i64, visitor: V);
134  pass!(deserialize_u8, visitor: V);
135  pass!(deserialize_u16, visitor: V);
136  pass!(deserialize_u32, visitor: V);
137  pass!(deserialize_u64, visitor: V);
138  pass!(deserialize_f32, visitor: V);
139  pass!(deserialize_f64, visitor: V);
140  pass!(deserialize_char, visitor: V);
141  pass!(deserialize_str, visitor: V);
142  pass!(deserialize_string, visitor: V);
143  pass!(deserialize_bytes, visitor: V);
144  pass!(deserialize_byte_buf, visitor: V);
145
146  fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
147    match &self.message.payload {
148      InvokeBody::Raw(_body) => Err(serde_json::Error::custom(format!(
149        "command {} expected a value for key {} but the IPC call used a bytes payload",
150        self.name, self.key
151      ))),
152      InvokeBody::Json(v) => match v.get(self.key) {
153        Some(value) => value.deserialize_option(visitor),
154        None => visitor.visit_none(),
155      },
156    }
157  }
158
159  pass!(deserialize_unit, visitor: V);
160  pass!(deserialize_unit_struct, name: &'static str, visitor: V);
161  pass!(deserialize_newtype_struct, name: &'static str, visitor: V);
162  pass!(deserialize_seq, visitor: V);
163  pass!(deserialize_tuple, len: usize, visitor: V);
164
165  pass!(
166    deserialize_tuple_struct,
167    name: &'static str,
168    len: usize,
169    visitor: V
170  );
171
172  pass!(deserialize_map, visitor: V);
173
174  pass!(
175    deserialize_struct,
176    name: &'static str,
177    fields: &'static [&'static str],
178    visitor: V
179  );
180
181  pass!(
182    deserialize_enum,
183    name: &'static str,
184    fields: &'static [&'static str],
185    visitor: V
186  );
187
188  pass!(deserialize_identifier, visitor: V);
189  pass!(deserialize_ignored_any, visitor: V);
190}
191
192/// [Autoref-based stable specialization](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md)
193///
194/// Nothing in this module is considered stable.
195#[doc(hidden)]
196pub mod private {
197  use crate::{
198    Runtime,
199    ipc::{InvokeError, InvokeResolver, InvokeResponseBody, IpcResponse},
200  };
201  use std::future::Future;
202  #[cfg(feature = "tracing")]
203  pub use tracing;
204
205  // ===== impl IpcResponse =====
206
207  pub struct ResponseTag;
208
209  pub trait ResponseKind {
210    #[inline(always)]
211    fn blocking_kind(&self) -> ResponseTag {
212      ResponseTag
213    }
214
215    #[inline(always)]
216    fn async_kind(&self) -> ResponseTag {
217      ResponseTag
218    }
219  }
220
221  impl<T: IpcResponse> ResponseKind for &T {}
222
223  impl ResponseTag {
224    #[inline(always)]
225    pub fn block<R, T>(self, value: T, resolver: InvokeResolver<R>)
226    where
227      R: Runtime,
228      T: IpcResponse,
229    {
230      resolver.respond(Ok(value))
231    }
232
233    #[inline(always)]
234    pub async fn future<T>(self, value: T) -> Result<InvokeResponseBody, InvokeError>
235    where
236      T: IpcResponse,
237    {
238      Ok(value.body()?)
239    }
240  }
241
242  // ===== Result<impl Serialize, impl Into<InvokeError>> =====
243
244  pub struct ResultTag;
245
246  pub trait ResultKind {
247    #[inline(always)]
248    fn blocking_kind(&self) -> ResultTag {
249      ResultTag
250    }
251
252    #[inline(always)]
253    fn async_kind(&self) -> ResultTag {
254      ResultTag
255    }
256  }
257
258  impl<T: IpcResponse, E: Into<InvokeError>> ResultKind for Result<T, E> {}
259
260  impl ResultTag {
261    #[inline(always)]
262    pub fn block<R, T, E>(self, value: Result<T, E>, resolver: InvokeResolver<R>)
263    where
264      R: Runtime,
265      T: IpcResponse,
266      E: Into<InvokeError>,
267    {
268      resolver.respond(value.map_err(Into::into))
269    }
270
271    #[inline(always)]
272    pub async fn future<T, E>(self, value: Result<T, E>) -> Result<InvokeResponseBody, InvokeError>
273    where
274      T: IpcResponse,
275      E: Into<InvokeError>,
276    {
277      Ok(value.map_err(Into::into)?.body()?)
278    }
279  }
280
281  // ===== Future<Output = impl IpcResponse> =====
282
283  pub struct FutureTag;
284
285  pub trait FutureKind {
286    #[inline(always)]
287    fn async_kind(&self) -> FutureTag {
288      FutureTag
289    }
290  }
291  impl<T: IpcResponse, F: Future<Output = T>> FutureKind for &F {}
292
293  impl FutureTag {
294    #[inline(always)]
295    pub async fn future<T, F>(self, value: F) -> Result<InvokeResponseBody, InvokeError>
296    where
297      T: IpcResponse,
298      F: Future<Output = T> + Send + 'static,
299    {
300      Ok(value.await.body()?)
301    }
302  }
303
304  // ===== Future<Output = Result<impl Serialize, impl Into<InvokeError>>> =====
305
306  pub struct ResultFutureTag;
307
308  pub trait ResultFutureKind {
309    #[inline(always)]
310    fn async_kind(&self) -> ResultFutureTag {
311      ResultFutureTag
312    }
313  }
314
315  impl<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>> ResultFutureKind
316    for F
317  {
318  }
319
320  impl ResultFutureTag {
321    #[inline(always)]
322    pub async fn future<T, E, F>(self, value: F) -> Result<InvokeResponseBody, InvokeError>
323    where
324      T: IpcResponse,
325      E: Into<InvokeError>,
326      F: Future<Output = Result<T, E>> + Send,
327    {
328      let response = value.await.map_err(Into::into)?;
329      Ok(response.body()?)
330    }
331  }
332}