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  ipc::{InvokeBody, InvokeError, InvokeMessage},
12  Runtime,
13};
14use serde::{
15  de::{Error, Visitor},
16  Deserialize, Deserializer,
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::Window`]
51/// * [`crate::State`]
52/// * `T where T: serde::Deserialize`
53///   * Any type that implements `Deserialize` can automatically be used as a [`CommandArg`].
54pub trait CommandArg<'de, R: Runtime>: Sized {
55  /// Derives an instance of `Self` from the [`CommandItem`].
56  ///
57  /// If the derivation fails, the corresponding message will be rejected using [`InvokeMessage#reject`].
58  fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError>;
59}
60
61/// Automatically implement [`CommandArg`] for any type that can be deserialized.
62impl<'de, D: Deserialize<'de>, R: Runtime> CommandArg<'de, R> for D {
63  fn from_command(command: CommandItem<'de, R>) -> Result<D, InvokeError> {
64    let name = command.name;
65    let arg = command.key;
66    #[cfg(feature = "tracing")]
67    let _span = tracing::trace_span!("ipc::request::deserialize_arg", arg = arg).entered();
68    Self::deserialize(command).map_err(|e| crate::Error::InvalidArgs(name, arg, e).into())
69  }
70}
71
72/// Pass the result of [`serde_json::Value::get`] into [`serde_json::Value`]'s deserializer.
73///
74/// Returns an error if the [`CommandItem`]'s key does not exist in the value.
75macro_rules! pass {
76  ($fn:ident, $($arg:ident: $argt:ty),+) => {
77    fn $fn<V: Visitor<'de>>(self, $($arg: $argt),*) -> Result<V::Value, Self::Error> {
78      self.deserialize_json()?.$fn($($arg),*)
79    }
80  }
81}
82
83impl<'a, R: Runtime> CommandItem<'a, R> {
84  fn deserialize_json(self) -> serde_json::Result<&'a serde_json::Value> {
85    if self.key.is_empty() {
86      return Err(serde_json::Error::custom(format!(
87        "command {} has an argument with no name with a non-optional value",
88        self.name
89      )));
90    }
91
92    match &self.message.payload {
93      InvokeBody::Raw(_body) => Err(serde_json::Error::custom(format!(
94        "command {} expected a value for key {} but the IPC call used a bytes payload",
95        self.name, self.key
96      ))),
97      InvokeBody::Json(v) => match v.get(self.key) {
98        Some(value) => Ok(value),
99        None => Err(serde_json::Error::custom(format!(
100          "command {} missing required key {}",
101          self.name, self.key
102        ))),
103      },
104    }
105  }
106}
107
108/// A [`Deserializer`] wrapper around [`CommandItem`].
109///
110/// If the key doesn't exist, an error will be returned if the deserialized type is not expecting
111/// an optional item. If the key does exist, the value will be called with
112/// [`Value`](serde_json::Value)'s [`Deserializer`] implementation.
113impl<'de, R: Runtime> Deserializer<'de> for CommandItem<'de, R> {
114  type Error = serde_json::Error;
115
116  pass!(deserialize_any, visitor: V);
117  pass!(deserialize_bool, visitor: V);
118  pass!(deserialize_i8, visitor: V);
119  pass!(deserialize_i16, visitor: V);
120  pass!(deserialize_i32, visitor: V);
121  pass!(deserialize_i64, visitor: V);
122  pass!(deserialize_u8, visitor: V);
123  pass!(deserialize_u16, visitor: V);
124  pass!(deserialize_u32, visitor: V);
125  pass!(deserialize_u64, visitor: V);
126  pass!(deserialize_f32, visitor: V);
127  pass!(deserialize_f64, visitor: V);
128  pass!(deserialize_char, visitor: V);
129  pass!(deserialize_str, visitor: V);
130  pass!(deserialize_string, visitor: V);
131  pass!(deserialize_bytes, visitor: V);
132  pass!(deserialize_byte_buf, visitor: V);
133
134  fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
135    match &self.message.payload {
136      InvokeBody::Raw(_body) => Err(serde_json::Error::custom(format!(
137        "command {} expected a value for key {} but the IPC call used a bytes payload",
138        self.name, self.key
139      ))),
140      InvokeBody::Json(v) => match v.get(self.key) {
141        Some(value) => value.deserialize_option(visitor),
142        None => visitor.visit_none(),
143      },
144    }
145  }
146
147  pass!(deserialize_unit, visitor: V);
148  pass!(deserialize_unit_struct, name: &'static str, visitor: V);
149  pass!(deserialize_newtype_struct, name: &'static str, visitor: V);
150  pass!(deserialize_seq, visitor: V);
151  pass!(deserialize_tuple, len: usize, visitor: V);
152
153  pass!(
154    deserialize_tuple_struct,
155    name: &'static str,
156    len: usize,
157    visitor: V
158  );
159
160  pass!(deserialize_map, visitor: V);
161
162  pass!(
163    deserialize_struct,
164    name: &'static str,
165    fields: &'static [&'static str],
166    visitor: V
167  );
168
169  pass!(
170    deserialize_enum,
171    name: &'static str,
172    fields: &'static [&'static str],
173    visitor: V
174  );
175
176  pass!(deserialize_identifier, visitor: V);
177  pass!(deserialize_ignored_any, visitor: V);
178}
179
180/// [Autoref-based stable specialization](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md)
181///
182/// Nothing in this module is considered stable.
183#[doc(hidden)]
184pub mod private {
185  use crate::{
186    ipc::{InvokeError, InvokeResolver, InvokeResponseBody, IpcResponse},
187    Runtime,
188  };
189  use std::future::Future;
190  #[cfg(feature = "tracing")]
191  pub use tracing;
192
193  // ===== impl IpcResponse =====
194
195  pub struct ResponseTag;
196
197  pub trait ResponseKind {
198    #[inline(always)]
199    fn blocking_kind(&self) -> ResponseTag {
200      ResponseTag
201    }
202
203    #[inline(always)]
204    fn async_kind(&self) -> ResponseTag {
205      ResponseTag
206    }
207  }
208
209  impl<T: IpcResponse> ResponseKind for &T {}
210
211  impl ResponseTag {
212    #[inline(always)]
213    pub fn block<R, T>(self, value: T, resolver: InvokeResolver<R>)
214    where
215      R: Runtime,
216      T: IpcResponse,
217    {
218      resolver.respond(Ok(value))
219    }
220
221    #[inline(always)]
222    pub async fn future<T>(self, value: T) -> Result<InvokeResponseBody, InvokeError>
223    where
224      T: IpcResponse,
225    {
226      Ok(value.body()?)
227    }
228  }
229
230  // ===== Result<impl Serialize, impl Into<InvokeError>> =====
231
232  pub struct ResultTag;
233
234  pub trait ResultKind {
235    #[inline(always)]
236    fn blocking_kind(&self) -> ResultTag {
237      ResultTag
238    }
239
240    #[inline(always)]
241    fn async_kind(&self) -> ResultTag {
242      ResultTag
243    }
244  }
245
246  impl<T: IpcResponse, E: Into<InvokeError>> ResultKind for Result<T, E> {}
247
248  impl ResultTag {
249    #[inline(always)]
250    pub fn block<R, T, E>(self, value: Result<T, E>, resolver: InvokeResolver<R>)
251    where
252      R: Runtime,
253      T: IpcResponse,
254      E: Into<InvokeError>,
255    {
256      resolver.respond(value.map_err(Into::into))
257    }
258
259    #[inline(always)]
260    pub async fn future<T, E>(self, value: Result<T, E>) -> Result<InvokeResponseBody, InvokeError>
261    where
262      T: IpcResponse,
263      E: Into<InvokeError>,
264    {
265      Ok(value.map_err(Into::into)?.body()?)
266    }
267  }
268
269  // ===== Future<Output = impl IpcResponse> =====
270
271  pub struct FutureTag;
272
273  pub trait FutureKind {
274    #[inline(always)]
275    fn async_kind(&self) -> FutureTag {
276      FutureTag
277    }
278  }
279  impl<T: IpcResponse, F: Future<Output = T>> FutureKind for &F {}
280
281  impl FutureTag {
282    #[inline(always)]
283    pub async fn future<T, F>(self, value: F) -> Result<InvokeResponseBody, InvokeError>
284    where
285      T: IpcResponse,
286      F: Future<Output = T> + Send + 'static,
287    {
288      Ok(value.await.body()?)
289    }
290  }
291
292  // ===== Future<Output = Result<impl Serialize, impl Into<InvokeError>>> =====
293
294  pub struct ResultFutureTag;
295
296  pub trait ResultFutureKind {
297    #[inline(always)]
298    fn async_kind(&self) -> ResultFutureTag {
299      ResultFutureTag
300    }
301  }
302
303  impl<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>> ResultFutureKind
304    for F
305  {
306  }
307
308  impl ResultFutureTag {
309    #[inline(always)]
310    pub async fn future<T, E, F>(self, value: F) -> Result<InvokeResponseBody, InvokeError>
311    where
312      T: IpcResponse,
313      E: Into<InvokeError>,
314      F: Future<Output = Result<T, E>> + Send,
315    {
316      let response = value.await.map_err(Into::into)?;
317      Ok(response.body()?)
318    }
319  }
320}