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