Skip to main content

tauri/
lib.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//! Tauri is a framework for building tiny, blazing fast binaries for all major desktop platforms.
6//! Developers can integrate any front-end framework that compiles to HTML, JS and CSS for building their user interface.
7//! The backend of the application is a rust-sourced binary with an API that the front-end can interact with.
8//!
9//! # Cargo features
10//!
11//! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that can be enabled or disabled:
12//!
13//! - **wry** *(enabled by default)*: Enables the [wry](https://github.com/tauri-apps/wry) runtime. Only disable it if you want a custom runtime.
14//! - **common-controls-v6** *(enabled by default)*: Enables [Common Controls v6](https://learn.microsoft.com/en-us/windows/win32/controls/common-control-versions) support on Windows, mainly for the predefined `about` menu item.
15//! - **x11** *(enabled by default)*: Enables X11 support. Disable this if you only target Wayland.
16//! - **dbus** *(enabled by default)*: Enables dbus dependency for theme support on Linux. Disable this if you do not need theme support or don't want to build the dbus rust crate. The WebView dependencies use dbus either way.
17//! - **unstable**: Enables unstable features. Be careful, it might introduce breaking changes in future minor releases.
18//! - **tracing**: Enables [`tracing`](https://docs.rs/tracing/latest/tracing) for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers.
19//! - **test**: Enables the [`mod@test`] module exposing unit test helpers.
20//! - **objc-exception**: This feature flag is no-op since 2.3.0.
21//! - **linux-libxdo**: Enables linking to libxdo which enables Cut, Copy, Paste and SelectAll menu items to work on Linux.
22//! - **isolation**: Enables the isolation pattern. Enabled by default if the `app > security > pattern > use` config option is set to `isolation` on the `tauri.conf.json` file.
23//! - **custom-protocol**: Feature managed by the Tauri CLI. When enabled, Tauri assumes a production environment instead of a development one.
24//! - **devtools**: Enables the developer tools (Web inspector) and [`window::Window#method.open_devtools`]. Enabled by default on debug builds.
25//!   On macOS it uses private APIs, so you can't enable it if your app will be published to the App Store.
26//! - **native-tls**: Provides TLS support to connect over HTTPS.
27//! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL.
28//! - **rustls-tls**: Provides TLS support to connect over HTTPS using rustls.
29//! - **process-relaunch-dangerous-allow-symlink-macos**: Allows the [`process::current_binary`] function to allow symlinks on macOS (this is dangerous, see the Security section in the documentation website).
30//! - **tray-icon**: Enables application tray icon APIs. Enabled by default if the `trayIcon` config is defined on the `tauri.conf.json` file.
31//! - **macos-private-api**: Enables features only available in **macOS**'s private APIs, currently the `transparent` window functionality and the `fullScreenEnabled` preference setting to `true`. Enabled by default if the `tauri > macosPrivateApi` config flag is set to `true` on the `tauri.conf.json` file.
32//! - **webview-data-url**: Enables usage of data URLs on the webview.
33//! - **compression** *(enabled by default): Enables asset compression. You should only disable this if you want faster compile times in release builds - it produces larger binaries.
34//! - **config-json5**: Adds support to JSON5 format for `tauri.conf.json`.
35//! - **config-toml**: Adds support to TOML format for the configuration `Tauri.toml`.
36//! - **image-ico**: Adds support to parse `.ico` image, see [`image::Image`].
37//! - **image-png**: Adds support to parse `.png` image, see [`image::Image`].
38//! - **macos-proxy**: Adds support for [`WebviewBuilder::proxy_url`] on macOS. Requires macOS 14+.
39//! - **specta**: Add support for [`specta::specta`](https://docs.rs/specta/%5E2.0.0-rc.9/specta/attr.specta.html) with Tauri arguments such as [`State`], [`Window`] and [`AppHandle`]
40//! - **dynamic-acl** *(enabled by default)*: Enables you to add ACLs at runtime, notably it enables the [`Manager::add_capability`] function.
41//!
42//! ## Cargo allowlist features
43//!
44//! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that enables commands for Tauri's API package.
45//! These features are automatically enabled by the Tauri CLI based on the `allowlist` configuration under `tauri.conf.json`.
46//!
47//! ### Protocol allowlist
48//!
49//! - **protocol-asset**: Enables the `asset` custom protocol.
50
51#![doc(
52  html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
53  html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
54)]
55#![warn(missing_docs, rust_2018_idioms)]
56#![cfg_attr(docsrs, feature(doc_cfg))]
57
58/// Setups the binding that initializes an iOS plugin.
59#[cfg(target_os = "ios")]
60#[macro_export]
61macro_rules! ios_plugin_binding {
62  ($fn_name: ident) => {
63    tauri::swift_rs::swift!(fn $fn_name() -> *const ::std::ffi::c_void);
64  }
65}
66#[cfg(target_os = "macos")]
67#[doc(hidden)]
68pub use embed_plist;
69pub use error::{Error, Result};
70use ipc::RuntimeAuthority;
71#[cfg(feature = "dynamic-acl")]
72use ipc::RuntimeCapability;
73pub use resources::{Resource, ResourceId, ResourceTable};
74#[cfg(target_os = "ios")]
75#[doc(hidden)]
76pub use swift_rs;
77pub use tauri_macros::include_image;
78#[cfg(mobile)]
79pub use tauri_macros::mobile_entry_point;
80pub use tauri_macros::{command, generate_handler};
81
82use tauri_utils::assets::AssetsIter;
83pub use url::Url;
84
85pub(crate) mod app;
86pub mod async_runtime;
87mod error;
88mod event;
89pub mod ipc;
90mod manager;
91mod pattern;
92pub mod plugin;
93pub(crate) mod protocol;
94mod resources;
95mod vibrancy;
96pub mod webview;
97pub mod window;
98use tauri_runtime as runtime;
99pub mod image;
100#[cfg(target_os = "ios")]
101mod ios;
102#[cfg(desktop)]
103#[cfg_attr(docsrs, doc(cfg(desktop)))]
104pub mod menu;
105/// Path APIs.
106pub mod path;
107pub mod process;
108/// The allowlist scopes.
109pub mod scope;
110mod state;
111
112#[cfg(all(desktop, feature = "tray-icon"))]
113#[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
114pub mod tray;
115pub use tauri_utils as utils;
116
117pub use http;
118
119/// A Tauri [`Runtime`] wrapper around wry.
120#[cfg(feature = "wry")]
121#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
122pub type Wry = tauri_runtime_wry::Wry<EventLoopMessage>;
123/// A Tauri [`RuntimeHandle`] wrapper around wry.
124#[cfg(feature = "wry")]
125#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
126pub type WryHandle = tauri_runtime_wry::WryHandle<EventLoopMessage>;
127
128#[cfg(all(feature = "wry", target_os = "android"))]
129#[cfg_attr(docsrs, doc(cfg(all(feature = "wry", target_os = "android"))))]
130#[doc(hidden)]
131#[macro_export]
132macro_rules! android_binding {
133  ($domain:ident, $app_name:ident, $main:ident, $wry:path) => {
134    use $wry::{
135      android_setup,
136      prelude::{JClass, JNIEnv, JString},
137    };
138
139    ::tauri::wry::android_binding!($domain, $app_name, $wry);
140
141    ::tauri::tao::android_binding!($domain, $app_name, Rust, android_setup, $main, ::tauri::tao);
142
143    // be careful when renaming this, the `Java_app_tauri_plugin_PluginManager_handlePluginResponse` symbol is checked by the CLI
144    ::tauri::tao::platform::android::prelude::android_fn!(
145      app_tauri,
146      plugin,
147      PluginManager,
148      handlePluginResponse,
149      [i32, JString, JString],
150    );
151    ::tauri::tao::platform::android::prelude::android_fn!(
152      app_tauri,
153      plugin,
154      PluginManager,
155      sendChannelData,
156      [i64, JString],
157    );
158
159    // this function is a glue between PluginManager.kt > handlePluginResponse and Rust
160    #[allow(non_snake_case)]
161    pub fn handlePluginResponse(
162      mut env: JNIEnv,
163      _: JClass,
164      id: i32,
165      success: JString,
166      error: JString,
167    ) {
168      ::tauri::handle_android_plugin_response(&mut env, id, success, error);
169    }
170
171    // this function is a glue between PluginManager.kt > sendChannelData and Rust
172    #[allow(non_snake_case)]
173    pub fn sendChannelData(mut env: JNIEnv, _: JClass, id: i64, data: JString) {
174      ::tauri::send_channel_data(&mut env, id, data);
175    }
176  };
177}
178
179#[cfg(all(feature = "wry", target_os = "android"))]
180#[doc(hidden)]
181pub use plugin::mobile::{handle_android_plugin_response, send_channel_data};
182#[cfg(all(feature = "wry", target_os = "android"))]
183#[doc(hidden)]
184pub use tauri_runtime_wry::{tao, wry};
185
186/// A task to run on the main thread.
187pub type SyncTask = Box<dyn FnOnce() + Send>;
188
189use serde::Serialize;
190use std::{
191  borrow::Cow,
192  collections::HashMap,
193  fmt::{self, Debug},
194  sync::MutexGuard,
195};
196use utils::assets::{AssetKey, CspHash, EmbeddedAssets};
197
198#[cfg(feature = "wry")]
199#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
200pub use tauri_runtime_wry::webview_version;
201
202#[cfg(target_os = "macos")]
203#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
204pub use runtime::ActivationPolicy;
205
206pub use self::utils::TitleBarStyle;
207
208use self::event::EventName;
209pub use self::event::{Event, EventId, EventTarget};
210use self::manager::EmitPayload;
211pub use {
212  self::app::{
213    App, AppHandle, AssetResolver, Builder, CloseRequestApi, ExitRequestApi, RunEvent,
214    UriSchemeContext, UriSchemeResponder, WebviewEvent, WindowEvent, RESTART_EXIT_CODE,
215  },
216  self::manager::Asset,
217  self::runtime::{
218    dpi::{
219      LogicalPosition, LogicalRect, LogicalSize, LogicalUnit, PhysicalPosition, PhysicalRect,
220      PhysicalSize, PhysicalUnit, Pixel, PixelUnit, Position, Rect, Size,
221    },
222    window::{CursorIcon, DragDropEvent, WindowSizeConstraints},
223    DeviceEventFilter, UserAttentionType,
224  },
225  self::state::{State, StateManager},
226  self::utils::{
227    config::{Config, WebviewUrl},
228    Env, PackageInfo, Theme,
229  },
230  self::webview::{Webview, WebviewWindow, WebviewWindowBuilder},
231  self::window::{Monitor, Window},
232  scope::*,
233};
234
235#[cfg(feature = "unstable")]
236#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
237pub use {self::webview::WebviewBuilder, self::window::WindowBuilder};
238
239/// The Tauri version.
240pub const VERSION: &str = env!("CARGO_PKG_VERSION");
241
242#[cfg(target_os = "ios")]
243#[doc(hidden)]
244pub fn log_stdout() {
245  #[cfg(target_os = "ios")]
246  unsafe {
247    crate::ios::log_stdout();
248  }
249}
250
251/// The user event type.
252#[derive(Debug, Clone)]
253pub enum EventLoopMessage {
254  /// An event from a menu item, could be on the window menu bar, application menu bar (on macOS) or tray icon menu.
255  #[cfg(desktop)]
256  MenuEvent(menu::MenuEvent),
257  /// An event from a menu item, could be on the window menu bar, application menu bar (on macOS) or tray icon menu.
258  #[cfg(all(desktop, feature = "tray-icon"))]
259  #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
260  TrayIconEvent(tray::TrayIconEvent),
261}
262
263/// The webview runtime interface. A wrapper around [`runtime::Runtime`] with the proper user event type associated.
264pub trait Runtime: runtime::Runtime<EventLoopMessage> {}
265/// The webview runtime handle. A wrapper around [`runtime::RuntimeHandle`] with the proper user event type associated.
266pub trait RuntimeHandle: runtime::RuntimeHandle<EventLoopMessage> {}
267
268impl<W: runtime::Runtime<EventLoopMessage>> Runtime for W {}
269impl<R: runtime::RuntimeHandle<EventLoopMessage>> RuntimeHandle for R {}
270
271/// Reads the config file at compile time and generates a [`Context`] based on its content.
272///
273/// The default config file path is a `tauri.conf.json` file inside the Cargo manifest directory of
274/// the crate being built.
275///
276/// # Custom Config Path
277///
278/// You may pass a string literal to this macro to specify a custom path for the Tauri config file.
279/// If the path is relative, it will be search for relative to the Cargo manifest of the compiling
280/// crate.
281///
282/// # Note
283///
284/// This macro should not be called if you are using [`tauri-build`] to generate the context from
285/// inside your build script as it will just cause excess computations that will be discarded. Use
286/// either the [`tauri-build`] method or this macro - not both.
287///
288/// [`tauri-build`]: https://docs.rs/tauri-build
289pub use tauri_macros::generate_context;
290
291/// Include a [`Context`] that was generated by [`tauri-build`] inside your build script.
292///
293/// You should either use [`tauri-build`] and this macro to include the compile time generated code,
294/// or [`generate_context!`]. Do not use both at the same time, as they generate the same code and
295/// will cause excess computations that will be discarded.
296///
297/// [`tauri-build`]: https://docs.rs/tauri-build
298#[macro_export]
299macro_rules! tauri_build_context {
300  () => {
301    include!(concat!(env!("OUT_DIR"), "/tauri-build-context.rs"))
302  };
303}
304
305pub use pattern::Pattern;
306
307/// Whether we are running in development mode or not.
308pub const fn is_dev() -> bool {
309  !cfg!(feature = "custom-protocol")
310}
311
312/// Represents a container of file assets that are retrievable during runtime.
313pub trait Assets<R: Runtime>: Send + Sync + 'static {
314  /// Initialize the asset provider.
315  fn setup(&self, app: &App<R>) {
316    let _ = app;
317  }
318
319  /// Get the content of the passed [`AssetKey`].
320  fn get(&self, key: &AssetKey) -> Option<Cow<'_, [u8]>>;
321
322  /// Iterator for the assets.
323  fn iter(&self) -> Box<tauri_utils::assets::AssetsIter<'_>>;
324
325  /// Gets the hashes for the CSP tag of the HTML on the given path.
326  fn csp_hashes(&self, html_path: &AssetKey) -> Box<dyn Iterator<Item = CspHash<'_>> + '_>;
327}
328
329impl<R: Runtime> Assets<R> for EmbeddedAssets {
330  fn get(&self, key: &AssetKey) -> Option<Cow<'_, [u8]>> {
331    EmbeddedAssets::get(self, key)
332  }
333
334  fn iter(&self) -> Box<AssetsIter<'_>> {
335    EmbeddedAssets::iter(self)
336  }
337
338  fn csp_hashes(&self, html_path: &AssetKey) -> Box<dyn Iterator<Item = CspHash<'_>> + '_> {
339    EmbeddedAssets::csp_hashes(self, html_path)
340  }
341}
342
343/// User supplied data required inside of a Tauri application.
344///
345/// # Stability
346/// This is the output of the [`generate_context`] macro, and is not considered part of the stable API.
347/// Unless you know what you are doing and are prepared for this type to have breaking changes, do not create it yourself.
348#[tauri_macros::default_runtime(Wry, wry)]
349pub struct Context<R: Runtime> {
350  pub(crate) config: Config,
351  #[cfg(dev)]
352  pub(crate) config_parent: Option<std::path::PathBuf>,
353  /// Asset provider.
354  pub assets: Box<dyn Assets<R>>,
355  pub(crate) default_window_icon: Option<image::Image<'static>>,
356  pub(crate) app_icon: Option<Vec<u8>>,
357  #[cfg(all(desktop, feature = "tray-icon"))]
358  pub(crate) tray_icon: Option<image::Image<'static>>,
359  pub(crate) package_info: PackageInfo,
360  pub(crate) pattern: Pattern,
361  pub(crate) runtime_authority: RuntimeAuthority,
362  pub(crate) plugin_global_api_scripts: Option<&'static [&'static str]>,
363}
364
365/// Temporary struct that overrides the Debug formatting for the `app_icon` field.
366///
367/// It reduces the output size compared to the default, as that would format the binary
368/// data as a slice of numbers `[65, 66, 67]`. This instead shows the length of the Vec.
369///
370/// For example: `Some([u8; 493])`
371pub(crate) struct DebugAppIcon<'a>(&'a Option<Vec<u8>>);
372
373impl std::fmt::Debug for DebugAppIcon<'_> {
374  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375    match self.0 {
376      Option::None => f.write_str("None"),
377      Option::Some(icon) => f
378        .debug_tuple("Some")
379        .field(&format_args!("[u8; {}]", icon.len()))
380        .finish(),
381    }
382  }
383}
384
385impl<R: Runtime> fmt::Debug for Context<R> {
386  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387    let mut d = f.debug_struct("Context");
388    d.field("config", &self.config)
389      .field("default_window_icon", &self.default_window_icon)
390      .field("app_icon", &DebugAppIcon(&self.app_icon))
391      .field("package_info", &self.package_info)
392      .field("pattern", &self.pattern)
393      .field("plugin_global_api_scripts", &self.plugin_global_api_scripts);
394
395    #[cfg(all(desktop, feature = "tray-icon"))]
396    d.field("tray_icon", &self.tray_icon);
397
398    d.finish()
399  }
400}
401
402impl<R: Runtime> Context<R> {
403  /// The config the application was prepared with.
404  #[inline(always)]
405  pub fn config(&self) -> &Config {
406    &self.config
407  }
408
409  /// A mutable reference to the config the application was prepared with.
410  #[inline(always)]
411  pub fn config_mut(&mut self) -> &mut Config {
412    &mut self.config
413  }
414
415  /// The assets to be served directly by Tauri.
416  #[inline(always)]
417  pub fn assets(&self) -> &dyn Assets<R> {
418    self.assets.as_ref()
419  }
420
421  /// Replace the [`Assets`] implementation and returns the previous value so you can use it as a fallback if desired.
422  #[inline(always)]
423  pub fn set_assets(&mut self, assets: Box<dyn Assets<R>>) -> Box<dyn Assets<R>> {
424    std::mem::replace(&mut self.assets, assets)
425  }
426
427  /// The default window icon Tauri should use when creating windows.
428  #[inline(always)]
429  pub fn default_window_icon(&self) -> Option<&image::Image<'_>> {
430    self.default_window_icon.as_ref()
431  }
432
433  /// Set the default window icon Tauri should use when creating windows.
434  #[inline(always)]
435  pub fn set_default_window_icon(&mut self, icon: Option<image::Image<'static>>) {
436    self.default_window_icon = icon;
437  }
438
439  /// The icon to use on the tray icon.
440  #[cfg(all(desktop, feature = "tray-icon"))]
441  #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
442  #[inline(always)]
443  pub fn tray_icon(&self) -> Option<&image::Image<'_>> {
444    self.tray_icon.as_ref()
445  }
446
447  /// Set the icon to use on the tray icon.
448  #[cfg(all(desktop, feature = "tray-icon"))]
449  #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
450  #[inline(always)]
451  pub fn set_tray_icon(&mut self, icon: Option<image::Image<'static>>) {
452    self.tray_icon = icon;
453  }
454
455  /// Package information.
456  #[inline(always)]
457  pub fn package_info(&self) -> &PackageInfo {
458    &self.package_info
459  }
460
461  /// A mutable reference to the package information.
462  #[inline(always)]
463  pub fn package_info_mut(&mut self) -> &mut PackageInfo {
464    &mut self.package_info
465  }
466
467  /// The application pattern.
468  #[inline(always)]
469  pub fn pattern(&self) -> &Pattern {
470    &self.pattern
471  }
472
473  /// A mutable reference to the resolved ACL.
474  ///
475  /// # Stability
476  ///
477  /// This API is unstable.
478  #[doc(hidden)]
479  #[inline(always)]
480  pub fn runtime_authority_mut(&mut self) -> &mut RuntimeAuthority {
481    &mut self.runtime_authority
482  }
483
484  /// Create a new [`Context`] from the minimal required items.
485  #[inline(always)]
486  #[allow(clippy::too_many_arguments)]
487  pub fn new(
488    config: Config,
489    assets: Box<dyn Assets<R>>,
490    default_window_icon: Option<image::Image<'static>>,
491    app_icon: Option<Vec<u8>>,
492    package_info: PackageInfo,
493    pattern: Pattern,
494    runtime_authority: RuntimeAuthority,
495    plugin_global_api_scripts: Option<&'static [&'static str]>,
496  ) -> Self {
497    Self {
498      config,
499      #[cfg(dev)]
500      config_parent: None,
501      assets,
502      default_window_icon,
503      app_icon,
504      #[cfg(all(desktop, feature = "tray-icon"))]
505      tray_icon: None,
506      package_info,
507      pattern,
508      runtime_authority,
509      plugin_global_api_scripts,
510    }
511  }
512
513  #[cfg(dev)]
514  #[doc(hidden)]
515  pub fn with_config_parent(&mut self, config_parent: impl AsRef<std::path::Path>) {
516    self
517      .config_parent
518      .replace(config_parent.as_ref().to_owned());
519  }
520}
521
522// TODO: expand these docs
523/// Manages a running application.
524pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
525  /// The application handle associated with this manager.
526  fn app_handle(&self) -> &AppHandle<R> {
527    self.managed_app_handle()
528  }
529
530  /// The [`Config`] the manager was created with.
531  fn config(&self) -> &Config {
532    self.manager().config()
533  }
534
535  /// The [`PackageInfo`] the manager was created with.
536  fn package_info(&self) -> &PackageInfo {
537    self.manager().package_info()
538  }
539
540  /// Fetch a single window from the manager.
541  #[cfg(feature = "unstable")]
542  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
543  fn get_window(&self, label: &str) -> Option<Window<R>> {
544    self.manager().get_window(label)
545  }
546
547  /// Fetch the focused window. Returns `None` if there is not any focused window.
548  #[cfg(feature = "unstable")]
549  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
550  fn get_focused_window(&self) -> Option<Window<R>> {
551    self.manager().get_focused_window()
552  }
553
554  /// Fetch all managed windows.
555  #[cfg(feature = "unstable")]
556  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
557  fn windows(&self) -> HashMap<String, Window<R>> {
558    self.manager().windows()
559  }
560
561  /// Fetch a single webview from the manager.
562  #[cfg(feature = "unstable")]
563  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
564  fn get_webview(&self, label: &str) -> Option<Webview<R>> {
565    self.manager().get_webview(label)
566  }
567
568  /// Fetch all managed webviews.
569  #[cfg(feature = "unstable")]
570  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
571  fn webviews(&self) -> HashMap<String, Webview<R>> {
572    self.manager().webviews()
573  }
574
575  /// Fetch a single webview window from the manager.
576  fn get_webview_window(&self, label: &str) -> Option<WebviewWindow<R>> {
577    self.manager().get_webview(label).and_then(|webview| {
578      let window = webview.window();
579      if window.is_webview_window() {
580        Some(WebviewWindow { window, webview })
581      } else {
582        None
583      }
584    })
585  }
586
587  /// Fetch all managed webview windows.
588  fn webview_windows(&self) -> HashMap<String, WebviewWindow<R>> {
589    self
590      .manager()
591      .webviews()
592      .into_iter()
593      .filter_map(|(label, webview)| {
594        let window = webview.window();
595        if window.is_webview_window() {
596          Some((label, WebviewWindow { window, webview }))
597        } else {
598          None
599        }
600      })
601      .collect::<HashMap<_, _>>()
602  }
603
604  /// Add `state` to the state managed by the application.
605  ///
606  /// If the state for the `T` type has previously been set, the state is unchanged and false is returned. Otherwise true is returned.
607  ///
608  /// Managed state can be retrieved by any command handler via the
609  /// [`State`] guard. In particular, if a value of type `T`
610  /// is managed by Tauri, adding `State<T>` to the list of arguments in a
611  /// command handler instructs Tauri to retrieve the managed value.
612  /// Additionally, [`state`](Self#method.state) can be used to retrieve the value manually.
613  ///
614  /// # Mutability
615  ///
616  /// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
617  ///
618  /// ```rust,no_run
619  /// use std::{collections::HashMap, sync::Mutex};
620  /// use tauri::State;
621  /// // here we use Mutex to achieve interior mutability
622  /// struct Storage {
623  ///   store: Mutex<HashMap<u64, String>>,
624  /// }
625  /// struct Connection;
626  /// struct DbConnection {
627  ///   db: Mutex<Option<Connection>>,
628  /// }
629  ///
630  /// #[tauri::command]
631  /// fn connect(connection: State<DbConnection>) {
632  ///   // initialize the connection, mutating the state with interior mutability
633  ///   *connection.db.lock().unwrap() = Some(Connection {});
634  /// }
635  ///
636  /// #[tauri::command]
637  /// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
638  ///   // mutate the storage behind the Mutex
639  ///   storage.store.lock().unwrap().insert(key, value);
640  /// }
641  ///
642  /// tauri::Builder::default()
643  ///   .manage(Storage { store: Default::default() })
644  ///   .manage(DbConnection { db: Default::default() })
645  ///   .invoke_handler(tauri::generate_handler![connect, storage_insert])
646  ///   // on an actual app, remove the string argument
647  ///   .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
648  ///   .expect("error while running tauri application");
649  /// ```
650  ///
651  /// # Examples
652  ///
653  /// ```rust,no_run
654  /// use tauri::{Manager, State};
655  ///
656  /// struct MyInt(isize);
657  /// struct MyString(String);
658  ///
659  /// #[tauri::command]
660  /// fn int_command(state: State<MyInt>) -> String {
661  ///     format!("The stateful int is: {}", state.0)
662  /// }
663  ///
664  /// #[tauri::command]
665  /// fn string_command<'r>(state: State<'r, MyString>) {
666  ///     println!("state: {}", state.inner().0);
667  /// }
668  ///
669  /// tauri::Builder::default()
670  ///   .setup(|app| {
671  ///     app.manage(MyInt(0));
672  ///     app.manage(MyString("tauri".into()));
673  ///     // `MyInt` is already managed, so `manage()` returns false
674  ///     assert!(!app.manage(MyInt(1)));
675  ///     // read the `MyInt` managed state with the turbofish syntax
676  ///     let int = app.state::<MyInt>();
677  ///     assert_eq!(int.0, 0);
678  ///     // read the `MyString` managed state with the `State` guard
679  ///     let val: State<MyString> = app.state();
680  ///     assert_eq!(val.0, "tauri");
681  ///     Ok(())
682  ///   })
683  ///   .invoke_handler(tauri::generate_handler![int_command, string_command])
684  ///   // on an actual app, remove the string argument
685  ///   .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
686  ///   .expect("error while running tauri application");
687  /// ```
688  fn manage<T>(&self, state: T) -> bool
689  where
690    T: Send + Sync + 'static,
691  {
692    self.manager().state().set(state)
693  }
694
695  /// Removes the state managed by the application for T. Returns the state if it was actually removed.
696  ///
697  /// <div class="warning">
698  ///
699  /// This method is *UNSAFE* and calling it will cause previously obtained references through
700  /// [Manager::state] and [State::inner] to become dangling references.
701  ///
702  /// It is currently deprecated and may be removed in the future.
703  ///
704  /// If you really want to unmanage a state, use [std::sync::Mutex] and [Option::take] to wrap the state instead.
705  ///
706  /// See [tauri-apps/tauri#12721] for more information.
707  ///
708  /// [tauri-apps/tauri#12721]: https://github.com/tauri-apps/tauri/issues/12721
709  ///
710  /// </div>
711  #[deprecated(
712    since = "2.3.0",
713    note = "This method is unsafe, since it can cause dangling references."
714  )]
715  fn unmanage<T>(&self) -> Option<T>
716  where
717    T: Send + Sync + 'static,
718  {
719    // The caller decides to break the safety here, then OK, just let it go.
720    unsafe { self.manager().state().unmanage() }
721  }
722
723  /// Retrieves the managed state for the type `T`.
724  ///
725  /// # Panics
726  ///
727  /// Panics if the state for the type `T` has not been previously [managed](Self::manage).
728  /// Use [try_state](Self::try_state) for a non-panicking version.
729  fn state<T>(&self) -> State<'_, T>
730  where
731    T: Send + Sync + 'static,
732  {
733    self.manager().state.try_get().unwrap_or_else(|| {
734      panic!(
735        "state() called before manage() for {}",
736        std::any::type_name::<T>()
737      )
738    })
739  }
740
741  /// Attempts to retrieve the managed state for the type `T`.
742  ///
743  /// Returns `Some` if the state has previously been [managed](Self::manage). Otherwise returns `None`.
744  fn try_state<T>(&self) -> Option<State<'_, T>>
745  where
746    T: Send + Sync + 'static,
747  {
748    self.manager().state.try_get()
749  }
750
751  /// Get a reference to the resources table of this manager.
752  fn resources_table(&self) -> MutexGuard<'_, ResourceTable>;
753
754  /// Gets the managed [`Env`].
755  fn env(&self) -> Env {
756    self.state::<Env>().inner().clone()
757  }
758
759  /// Gets the scope for the asset protocol.
760  #[cfg(feature = "protocol-asset")]
761  fn asset_protocol_scope(&self) -> scope::fs::Scope {
762    self.state::<Scopes>().inner().asset_protocol.clone()
763  }
764
765  /// The path resolver.
766  fn path(&self) -> &crate::path::PathResolver<R> {
767    self.state::<crate::path::PathResolver<R>>().inner()
768  }
769
770  /// Adds a capability to the app.
771  ///
772  /// Note that by default every capability file in the `src-tauri/capabilities` folder
773  /// are automatically enabled unless specific capabilities are configured in [`tauri.conf.json > app > security > capabilities`],
774  /// so you should use a different director for the runtime-added capabilities or use [tauri_build::Attributes::capabilities_path_pattern].
775  ///
776  /// # Examples
777  /// ```
778  /// use tauri::Manager;
779  ///
780  /// tauri::Builder::default()
781  ///   .setup(|app| {
782  ///     #[cfg(feature = "beta")]
783  ///     app.add_capability(include_str!("../capabilities/beta/cap.json"));
784  ///
785  ///     #[cfg(feature = "stable")]
786  ///     app.add_capability(include_str!("../capabilities/stable/cap.json"));
787  ///     Ok(())
788  ///   });
789  /// ```
790  ///
791  /// The above example assumes the following directory layout:
792  /// ```md
793  /// ├── capabilities
794  /// │   ├── app (default capabilities used by any app flavor)
795  /// |   |   |-- cap.json
796  /// │   ├── beta (capabilities only added to a `beta` flavor)
797  /// |   |   |-- cap.json
798  /// │   ├── stable (capabilities only added to a `stable` flavor)
799  /// |       |-- cap.json
800  /// ```
801  ///
802  /// For this layout to be properly parsed by Tauri, we need to change the build script to
803  ///
804  /// ```skip
805  /// // only pick up capabilities in the capabilities/app folder by default
806  /// let attributes = tauri_build::Attributes::new().capabilities_path_pattern("./capabilities/app/*.json");
807  /// tauri_build::try_build(attributes).unwrap();
808  /// ```
809  ///
810  /// [`tauri.conf.json > app > security > capabilities`]: https://tauri.app/reference/config/#capabilities
811  /// [tauri_build::Attributes::capabilities_path_pattern]: https://docs.rs/tauri-build/2/tauri_build/struct.Attributes.html#method.capabilities_path_pattern
812  #[cfg(feature = "dynamic-acl")]
813  fn add_capability(&self, capability: impl RuntimeCapability) -> Result<()> {
814    self
815      .manager()
816      .runtime_authority
817      .lock()
818      .unwrap()
819      .add_capability(capability)
820  }
821}
822
823/// Listen to events.
824pub trait Listener<R: Runtime>: sealed::ManagerBase<R> {
825  /// Listen to an emitted event on this manager.
826  ///
827  /// # Examples
828  /// ```
829  /// use tauri::{Manager, Listener, Emitter};
830  ///
831  /// #[tauri::command]
832  /// fn synchronize(window: tauri::Window) {
833  ///   // emits the synchronized event to all windows
834  ///   window.emit("synchronized", ());
835  /// }
836  ///
837  /// tauri::Builder::default()
838  ///   .setup(|app| {
839  ///     app.listen("synchronized", |event| {
840  ///       println!("app is in sync");
841  ///     });
842  ///     Ok(())
843  ///   })
844  ///   .invoke_handler(tauri::generate_handler![synchronize]);
845  /// ```
846  /// # Panics
847  /// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
848  fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
849  where
850    F: Fn(Event) + Send + 'static;
851
852  /// Listen to an event on this manager only once.
853  ///
854  /// See [`Self::listen`] for more information.
855  /// # Panics
856  /// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
857  fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
858  where
859    F: FnOnce(Event) + Send + 'static;
860
861  /// Remove an event listener.
862  ///
863  /// # Examples
864  /// ```
865  /// use tauri::{Manager, Listener};
866  ///
867  /// tauri::Builder::default()
868  ///   .setup(|app| {
869  ///     let handle = app.handle().clone();
870  ///     let handler = app.listen_any("ready", move |event| {
871  ///       println!("app is ready");
872  ///
873  ///       // we no longer need to listen to the event
874  ///       // we also could have used `app.once_global` instead
875  ///       handle.unlisten(event.id());
876  ///     });
877  ///
878  ///     // stop listening to the event when you do not need it anymore
879  ///     app.unlisten(handler);
880  ///
881  ///
882  ///     Ok(())
883  ///   });
884  /// ```
885  fn unlisten(&self, id: EventId);
886
887  /// Listen to an emitted event to any [target](EventTarget).
888  ///
889  /// # Examples
890  /// ```
891  /// use tauri::{Manager, Emitter, Listener};
892  ///
893  /// #[tauri::command]
894  /// fn synchronize(window: tauri::Window) {
895  ///   // emits the synchronized event to all windows
896  ///   window.emit("synchronized", ());
897  /// }
898  ///
899  /// tauri::Builder::default()
900  ///   .setup(|app| {
901  ///     app.listen_any("synchronized", |event| {
902  ///       println!("app is in sync");
903  ///     });
904  ///     Ok(())
905  ///   })
906  ///   .invoke_handler(tauri::generate_handler![synchronize]);
907  /// ```
908  /// # Panics
909  /// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
910  fn listen_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
911  where
912    F: Fn(Event) + Send + 'static,
913  {
914    let event = EventName::new(event.into()).unwrap();
915    self.manager().listen(event, EventTarget::Any, handler)
916  }
917
918  /// Listens once to an emitted event to any [target](EventTarget) .
919  ///
920  /// See [`Self::listen_any`] for more information.
921  /// # Panics
922  /// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
923  fn once_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
924  where
925    F: FnOnce(Event) + Send + 'static,
926  {
927    let event = EventName::new(event.into()).unwrap();
928    self.manager().once(event, EventTarget::Any, handler)
929  }
930}
931
932/// Emit events.
933pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
934  /// Emits an event to all [targets](EventTarget).
935  ///
936  /// # Examples
937  /// ```
938  /// use tauri::Emitter;
939  ///
940  /// #[tauri::command]
941  /// fn synchronize(app: tauri::AppHandle) {
942  ///   // emits the synchronized event to all webviews
943  ///   app.emit("synchronized", ());
944  /// }
945  /// ```
946  fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
947    let event = EventName::new(event)?;
948    let payload = EmitPayload::Serialize(&payload);
949    self.manager().emit(event, payload)
950  }
951
952  /// Similar to [`Emitter::emit`] but the payload is json serialized.
953  fn emit_str(&self, event: &str, payload: String) -> Result<()> {
954    let event = EventName::new(event)?;
955    let payload = EmitPayload::<()>::Str(payload);
956    self.manager().emit(event, payload)
957  }
958
959  /// Emits an event to all [targets](EventTarget) matching the given target.
960  ///
961  /// # Examples
962  /// ```
963  /// use tauri::{Emitter, EventTarget};
964  ///
965  /// #[tauri::command]
966  /// fn download(app: tauri::AppHandle) {
967  ///   for i in 1..100 {
968  ///     std::thread::sleep(std::time::Duration::from_millis(150));
969  ///     // emit a download progress event to all listeners
970  ///     app.emit_to(EventTarget::any(), "download-progress", i);
971  ///     // emit an event to listeners that used App::listen or AppHandle::listen
972  ///     app.emit_to(EventTarget::app(), "download-progress", i);
973  ///     // emit an event to any webview/window/webviewWindow matching the given label
974  ///     app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
975  ///     app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
976  ///     // emit an event to listeners that used WebviewWindow::listen
977  ///     app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
978  ///   }
979  /// }
980  /// ```
981  fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
982  where
983    I: Into<EventTarget>,
984    S: Serialize + Clone,
985  {
986    let event = EventName::new(event)?;
987    let payload = EmitPayload::Serialize(&payload);
988    self.manager().emit_to(target.into(), event, payload)
989  }
990
991  /// Similar to [`Emitter::emit_to`] but the payload is json serialized.
992  fn emit_str_to<I>(&self, target: I, event: &str, payload: String) -> Result<()>
993  where
994    I: Into<EventTarget>,
995  {
996    let event = EventName::new(event)?;
997    let payload = EmitPayload::<()>::Str(payload);
998    self.manager().emit_to(target.into(), event, payload)
999  }
1000
1001  /// Emits an event to all [targets](EventTarget) based on the given filter.
1002  ///
1003  /// # Examples
1004  /// ```
1005  /// use tauri::{Emitter, EventTarget};
1006  ///
1007  /// #[tauri::command]
1008  /// fn download(app: tauri::AppHandle) {
1009  ///   for i in 1..100 {
1010  ///     std::thread::sleep(std::time::Duration::from_millis(150));
1011  ///     // emit a download progress event to the updater window
1012  ///     app.emit_filter("download-progress", i, |t| match t {
1013  ///       EventTarget::WebviewWindow { label } => label == "main",
1014  ///       _ => false,
1015  ///     });
1016  ///   }
1017  /// }
1018  /// ```
1019  fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
1020  where
1021    S: Serialize + Clone,
1022    F: Fn(&EventTarget) -> bool,
1023  {
1024    let event = EventName::new(event)?;
1025    let payload = EmitPayload::Serialize(&payload);
1026    self.manager().emit_filter(event, payload, filter)
1027  }
1028
1029  /// Similar to [`Emitter::emit_filter`] but the payload is json serialized.
1030  fn emit_str_filter<F>(&self, event: &str, payload: String, filter: F) -> Result<()>
1031  where
1032    F: Fn(&EventTarget) -> bool,
1033  {
1034    let event = EventName::new(event)?;
1035    let payload = EmitPayload::<()>::Str(payload);
1036    self.manager().emit_filter(event, payload, filter)
1037  }
1038}
1039
1040/// Prevent implementation details from leaking out of the [`Manager`] trait.
1041pub(crate) mod sealed {
1042  use super::Runtime;
1043  use crate::{app::AppHandle, manager::AppManager};
1044  use std::sync::Arc;
1045
1046  /// A running [`Runtime`] or a dispatcher to it.
1047  pub enum RuntimeOrDispatch<'r, R: Runtime> {
1048    /// Reference to the running [`Runtime`].
1049    Runtime(&'r R),
1050
1051    /// Handle to the running [`Runtime`].
1052    RuntimeHandle(R::Handle),
1053
1054    /// A dispatcher to the running [`Runtime`].
1055    Dispatch(R::WindowDispatcher),
1056  }
1057
1058  /// Managed handle to the application runtime.
1059  pub trait ManagerBase<R: Runtime> {
1060    fn manager(&self) -> &AppManager<R>;
1061    fn manager_owned(&self) -> Arc<AppManager<R>>;
1062    fn runtime(&self) -> RuntimeOrDispatch<'_, R>;
1063    fn managed_app_handle(&self) -> &AppHandle<R>;
1064    #[cfg(target_os = "android")]
1065    fn activity_name(&self) -> Option<crate::Result<String>>;
1066    #[cfg(target_os = "ios")]
1067    fn scene_identifier(&self) -> Option<crate::Result<String>>;
1068  }
1069}
1070
1071struct UnsafeSend<T>(T);
1072unsafe impl<T> Send for UnsafeSend<T> {}
1073
1074impl<T> UnsafeSend<T> {
1075  fn take(self) -> T {
1076    self.0
1077  }
1078}
1079
1080#[allow(unused)]
1081macro_rules! run_main_thread {
1082  ($handle:ident, $ex:expr) => {{
1083    use std::sync::mpsc::channel;
1084    let (tx, rx) = channel();
1085    let task = move || {
1086      let f = $ex;
1087      let _ = tx.send(f());
1088    };
1089    $handle
1090      .run_on_main_thread(task)
1091      .and_then(|_| rx.recv().map_err(|_| crate::Error::FailedToReceiveMessage))
1092  }};
1093}
1094
1095#[allow(unused)]
1096pub(crate) use run_main_thread;
1097
1098#[cfg(any(test, feature = "test"))]
1099#[cfg_attr(docsrs, doc(cfg(feature = "test")))]
1100pub mod test;
1101
1102#[cfg(feature = "specta")]
1103const _: () = {
1104  use specta::{datatype::DataType, function::FunctionArg, TypeMap};
1105
1106  impl<T: Send + Sync + 'static> FunctionArg for crate::State<'_, T> {
1107    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1108      None
1109    }
1110  }
1111
1112  impl<R: crate::Runtime> FunctionArg for crate::AppHandle<R> {
1113    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1114      None
1115    }
1116  }
1117
1118  impl<R: crate::Runtime> FunctionArg for crate::Window<R> {
1119    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1120      None
1121    }
1122  }
1123
1124  impl<R: crate::Runtime> FunctionArg for crate::Webview<R> {
1125    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1126      None
1127    }
1128  }
1129
1130  impl<R: crate::Runtime> FunctionArg for crate::WebviewWindow<R> {
1131    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1132      None
1133    }
1134  }
1135};
1136
1137#[cfg(test)]
1138mod tests {
1139  use cargo_toml::Manifest;
1140  use std::{env::var, fs::read_to_string, path::PathBuf, sync::OnceLock};
1141
1142  static MANIFEST: OnceLock<Manifest> = OnceLock::new();
1143  const CHECKED_FEATURES: &str = include_str!(concat!(env!("OUT_DIR"), "/checked_features"));
1144
1145  fn get_manifest() -> &'static Manifest {
1146    MANIFEST.get_or_init(|| {
1147      let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
1148      Manifest::from_path(manifest_dir.join("Cargo.toml")).expect("failed to parse Cargo manifest")
1149    })
1150  }
1151
1152  #[test]
1153  fn features_are_documented() {
1154    let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
1155    let lib_code = read_to_string(manifest_dir.join("src/lib.rs")).expect("failed to read lib.rs");
1156
1157    for f in get_manifest().features.keys() {
1158      if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{f}**"))) {
1159        panic!("Feature {f} is not documented");
1160      }
1161    }
1162  }
1163
1164  #[test]
1165  fn aliased_features_exist() {
1166    let checked_features = CHECKED_FEATURES.split(',');
1167    let manifest = get_manifest();
1168    for checked_feature in checked_features {
1169      if !manifest.features.iter().any(|(f, _)| f == checked_feature) {
1170        panic!(
1171          "Feature {checked_feature} was checked in the alias build step but it does not exist in crates/tauri/Cargo.toml"
1172        );
1173      }
1174    }
1175  }
1176}
1177
1178#[cfg(test)]
1179mod test_utils {
1180  use proptest::prelude::*;
1181
1182  pub fn assert_send<T: Send>() {}
1183  pub fn assert_sync<T: Sync>() {}
1184
1185  #[allow(dead_code)]
1186  pub fn assert_not_allowlist_error<T>(res: anyhow::Result<T>) {
1187    if let Err(e) = res {
1188      assert!(!e.to_string().contains("not on the allowlist"));
1189    }
1190  }
1191
1192  proptest! {
1193    #![proptest_config(ProptestConfig::with_cases(10000))]
1194    #[test]
1195    // check to see if spawn executes a function.
1196    fn check_spawn_task(task in "[a-z]+") {
1197      // create dummy task function
1198      let dummy_task = async move {
1199        let _ = format!("{task}-run-dummy-task");
1200      };
1201      // call spawn
1202      crate::async_runtime::spawn(dummy_task);
1203    }
1204  }
1205}
1206
1207/// Simple dependency-free string encoder using [Z85].
1208mod z85 {
1209  const TABLE: &[u8; 85] =
1210    b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#";
1211
1212  /// Encode bytes with [Z85].
1213  ///
1214  /// # Panics
1215  ///
1216  /// Will panic if the input bytes are not a multiple of 4.
1217  pub fn encode(bytes: &[u8]) -> String {
1218    assert_eq!(bytes.len() % 4, 0);
1219
1220    let mut buf = String::with_capacity(bytes.len() * 5 / 4);
1221    for chunk in bytes.chunks_exact(4) {
1222      let mut chars = [0u8; 5];
1223      let mut chunk = u32::from_be_bytes(chunk.try_into().unwrap()) as usize;
1224      for byte in chars.iter_mut().rev() {
1225        *byte = TABLE[chunk % 85];
1226        chunk /= 85;
1227      }
1228
1229      buf.push_str(std::str::from_utf8(&chars).unwrap());
1230    }
1231
1232    buf
1233  }
1234
1235  #[cfg(test)]
1236  mod tests {
1237    #[test]
1238    fn encode() {
1239      assert_eq!(
1240        super::encode(&[0x86, 0x4F, 0xD2, 0x6F, 0xB5, 0x59, 0xF7, 0x5B]),
1241        "HelloWorld"
1242      );
1243    }
1244  }
1245}
1246
1247/// Generate a random 128-bit [Z85] encoded [`String`].
1248///
1249/// [Z85]: https://rfc.zeromq.org/spec/32/
1250pub(crate) fn generate_invoke_key() -> Result<String> {
1251  let mut bytes = [0u8; 16];
1252  getrandom::fill(&mut bytes)?;
1253  Ok(z85::encode(&bytes))
1254}