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      if self.key.is_empty() {
79        return Err(serde_json::Error::custom(format!(
80            "command {} has an argument with no name with a non-optional value",
81            self.name
82          )))
83      }
84
85      match &self.message.payload {
86        InvokeBody::Raw(_body) => {
87          Err(serde_json::Error::custom(format!(
88            "command {} expected a value for key {} but the IPC call used a bytes payload",
89            self.name, self.key
90          )))
91        }
92        InvokeBody::Json(v) => {
93          match v.get(self.key) {
94            Some(value) => value.$fn($($arg),*),
95            None => {
96              Err(serde_json::Error::custom(format!(
97                "command {} missing required key {}",
98                self.name, self.key
99              )))
100            }
101          }
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 futures_util::{FutureExt, TryFutureExt};
190  use std::future::Future;
191  #[cfg(feature = "tracing")]
192  pub use tracing;
193
194  // ===== impl IpcResponse =====
195
196  pub struct ResponseTag;
197
198  pub trait ResponseKind {
199    #[inline(always)]
200    fn blocking_kind(&self) -> ResponseTag {
201      ResponseTag
202    }
203
204    #[inline(always)]
205    fn async_kind(&self) -> ResponseTag {
206      ResponseTag
207    }
208  }
209
210  impl<T: IpcResponse> ResponseKind for &T {}
211
212  impl ResponseTag {
213    #[inline(always)]
214    pub fn block<R, T>(self, value: T, resolver: InvokeResolver<R>)
215    where
216      R: Runtime,
217      T: IpcResponse,
218    {
219      resolver.respond(Ok(value))
220    }
221
222    #[inline(always)]
223    pub fn future<T>(
224      self,
225      value: T,
226    ) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
227    where
228      T: IpcResponse,
229    {
230      std::future::ready(value.body().map_err(InvokeError::from_error))
231    }
232  }
233
234  // ===== Result<impl Serialize, impl Into<InvokeError>> =====
235
236  pub struct ResultTag;
237
238  pub trait ResultKind {
239    #[inline(always)]
240    fn blocking_kind(&self) -> ResultTag {
241      ResultTag
242    }
243
244    #[inline(always)]
245    fn async_kind(&self) -> ResultTag {
246      ResultTag
247    }
248  }
249
250  impl<T: IpcResponse, E: Into<InvokeError>> ResultKind for Result<T, E> {}
251
252  impl ResultTag {
253    #[inline(always)]
254    pub fn block<R, T, E>(self, value: Result<T, E>, resolver: InvokeResolver<R>)
255    where
256      R: Runtime,
257      T: IpcResponse,
258      E: Into<InvokeError>,
259    {
260      resolver.respond(value.map_err(Into::into))
261    }
262
263    #[inline(always)]
264    pub fn future<T, E>(
265      self,
266      value: Result<T, E>,
267    ) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
268    where
269      T: IpcResponse,
270      E: Into<InvokeError>,
271    {
272      std::future::ready(
273        value
274          .map_err(Into::into)
275          .and_then(|value| value.body().map_err(InvokeError::from_error)),
276      )
277    }
278  }
279
280  // ===== Future<Output = impl IpcResponse> =====
281
282  pub struct FutureTag;
283
284  pub trait FutureKind {
285    #[inline(always)]
286    fn async_kind(&self) -> FutureTag {
287      FutureTag
288    }
289  }
290  impl<T: IpcResponse, F: Future<Output = T>> FutureKind for &F {}
291
292  impl FutureTag {
293    #[inline(always)]
294    pub fn future<T, F>(
295      self,
296      value: F,
297    ) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
298    where
299      T: IpcResponse,
300      F: Future<Output = T> + Send + 'static,
301    {
302      value.map(|value| value.body().map_err(InvokeError::from_error))
303    }
304  }
305
306  // ===== Future<Output = Result<impl Serialize, impl Into<InvokeError>>> =====
307
308  pub struct ResultFutureTag;
309
310  pub trait ResultFutureKind {
311    #[inline(always)]
312    fn async_kind(&self) -> ResultFutureTag {
313      ResultFutureTag
314    }
315  }
316
317  impl<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>> ResultFutureKind
318    for F
319  {
320  }
321
322  impl ResultFutureTag {
323    #[inline(always)]
324    pub fn future<T, E, F>(
325      self,
326      value: F,
327    ) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
328    where
329      T: IpcResponse,
330      E: Into<InvokeError>,
331      F: Future<Output = Result<T, E>> + Send,
332    {
333      value
334        .err_into()
335        .map(|result| result.and_then(|value| value.body().map_err(InvokeError::from_error)))
336    }
337  }
338}