lib.rs 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! Tauri is a framework for building tiny, blazing fast binaries for all major desktop platforms.
  5. //! Developers can integrate any front-end framework that compiles to HTML, JS and CSS for building their user interface.
  6. //! The backend of the application is a rust-sourced binary with an API that the front-end can interact with.
  7. //!
  8. //! # Cargo features
  9. //!
  10. //! 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:
  11. //!
  12. //! - **wry** *(enabled by default)*: Enables the [wry](https://github.com/tauri-apps/wry) runtime. Only disable it if you want a custom runtime.
  13. //! - **tracing**: Enables [`tracing`](https://docs.rs/tracing/latest/tracing) for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers.
  14. //! - **test**: Enables the [`test`] module exposing unit test helpers.
  15. //! - **dox**: Internal feature to generate Rust documentation without linking on Linux.
  16. //! - **objc-exception**: Wrap each msg_send! in a @try/@catch and panics if an exception is caught, preventing Objective-C from unwinding into Rust.
  17. //! - **linux-protocol-headers**: Enables headers support for custom protocol requests on Linux. Requires webkit2gtk v2.36 or above.
  18. //! - **isolation**: Enables the isolation pattern. Enabled by default if the `tauri > pattern > use` config option is set to `isolation` on the `tauri.conf.json` file.
  19. //! - **custom-protocol**: Feature managed by the Tauri CLI. When enabled, Tauri assumes a production environment instead of a development one.
  20. //! - **updater**: Enables the application auto updater. Enabled by default if the `updater` config is defined on the `tauri.conf.json` file.
  21. //! - **devtools**: Enables the developer tools (Web inspector) and [`Window::open_devtools`]. Enabled by default on debug builds.
  22. //! On macOS it uses private APIs, so you can't enable it if your app will be published to the App Store.
  23. //! - **shell-open-api**: Enables the [`api::shell`] module.
  24. //! - **http-api**: Enables the [`api::http`] module.
  25. //! - **http-multipart**: Adds support to `multipart/form-data` requests.
  26. //! - **reqwest-client**: Alias for the `http-api` feature flag.
  27. //! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL.
  28. //! - **reqwest-native-tls-vendored**: Alias for the `native-tls-vendored` feature flag.
  29. //! - **os-api**: Enables the [`api::os`] module.
  30. //! - **process-command-api**: Enables the [`api::process::Command`] APIs.
  31. //! - **global-shortcut**: Enables the global shortcut APIs.
  32. //! - **clipboard**: Enables the clipboard APIs.
  33. //! - **process-relaunch-dangerous-allow-symlink-macos**: Allows the [`api::process::current_binary`] function to allow symlinks on macOS (this is dangerous, see the Security section in the documentation website).
  34. //! - **dialog**: Enables the [`api::dialog`] module.
  35. //! - **notification**: Enables the [`api::notification`] module.
  36. //! - **fs-extract-api**: Enabled the `tauri::api::file::Extract` API.
  37. //! - **cli**: Enables usage of `clap` for CLI argument parsing. Enabled by default if the `cli` config is defined on the `tauri.conf.json` file.
  38. //! - **system-tray**: Enables application system tray API. Enabled by default if the `systemTray` config is defined on the `tauri.conf.json` file.
  39. //! - **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.
  40. //! - **windows7-compat**: Enables compatibility with Windows 7 for the notification API.
  41. //! - **window-data-url**: Enables usage of data URLs on the webview.
  42. //! - **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.
  43. //! - **config-json5**: Adds support to JSON5 format for `tauri.conf.json`.
  44. //! - **config-toml**: Adds support to TOML format for the configuration `Tauri.toml`.
  45. //! - **icon-ico**: Adds support to set `.ico` window icons. Enables [`Icon::File`] and [`Icon::Raw`] variants.
  46. //! - **icon-png**: Adds support to set `.png` window icons. Enables [`Icon::File`] and [`Icon::Raw`] variants.
  47. //!
  48. //! ## Cargo allowlist features
  49. //!
  50. //! 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.
  51. //! These features are automatically enabled by the Tauri CLI based on the `allowlist` configuration under `tauri.conf.json`.
  52. //!
  53. //! - **api-all**: Enables all API endpoints.
  54. //!
  55. //! ### Clipboard allowlist
  56. //!
  57. //! - **clipboard-all**: Enables all [Clipboard APIs](https://tauri.app/en/docs/api/js/modules/clipboard/).
  58. //! - **clipboard-read-text**: Enables the [`readText` API](https://tauri.app/en/docs/api/js/modules/clipboard/#readtext).
  59. //! - **clipboard-write-text**: Enables the [`writeText` API](https://tauri.app/en/docs/api/js/modules/clipboard/#writetext).
  60. //!
  61. //! ### Dialog allowlist
  62. //!
  63. //! - **dialog-all**: Enables all [Dialog APIs](https://tauri.app/en/docs/api/js/modules/dialog).
  64. //! - **dialog-ask**: Enables the [`ask` API](https://tauri.app/en/docs/api/js/modules/dialog#ask).
  65. //! - **dialog-confirm**: Enables the [`confirm` API](https://tauri.app/en/docs/api/js/modules/dialog#confirm).
  66. //! - **dialog-message**: Enables the [`message` API](https://tauri.app/en/docs/api/js/modules/dialog#message).
  67. //! - **dialog-open**: Enables the [`open` API](https://tauri.app/en/docs/api/js/modules/dialog#open).
  68. //! - **dialog-save**: Enables the [`save` API](https://tauri.app/en/docs/api/js/modules/dialog#save).
  69. //!
  70. //! ### Filesystem allowlist
  71. //!
  72. //! - **fs-all**: Enables all [Filesystem APIs](https://tauri.app/en/docs/api/js/modules/fs).
  73. //! - **fs-copy-file**: Enables the [`copyFile` API](https://tauri.app/en/docs/api/js/modules/fs#copyfile).
  74. //! - **fs-create-dir**: Enables the [`createDir` API](https://tauri.app/en/docs/api/js/modules/fs#createdir).
  75. //! - **fs-exists**: Enables the [`exists` API](https://tauri.app/en/docs/api/js/modules/fs#exists).
  76. //! - **fs-read-dir**: Enables the [`readDir` API](https://tauri.app/en/docs/api/js/modules/fs#readdir).
  77. //! - **fs-read-file**: Enables the [`readTextFile` API](https://tauri.app/en/docs/api/js/modules/fs#readtextfile) and the [`readBinaryFile` API](https://tauri.app/en/docs/api/js/modules/fs#readbinaryfile).
  78. //! - **fs-remove-dir**: Enables the [`removeDir` API](https://tauri.app/en/docs/api/js/modules/fs#removedir).
  79. //! - **fs-remove-file**: Enables the [`removeFile` API](https://tauri.app/en/docs/api/js/modules/fs#removefile).
  80. //! - **fs-rename-file**: Enables the [`renameFile` API](https://tauri.app/en/docs/api/js/modules/fs#renamefile).
  81. //! - **fs-write-file**: Enables the [`writeFile` API](https://tauri.app/en/docs/api/js/modules/fs#writefile) and the [`writeBinaryFile` API](https://tauri.app/en/docs/api/js/modules/fs#writebinaryfile).
  82. //!
  83. //! ### Global shortcut allowlist
  84. //!
  85. //! - **global-shortcut-all**: Enables all [GlobalShortcut APIs](https://tauri.app/en/docs/api/js/modules/globalShortcut).
  86. //!
  87. //! ### HTTP allowlist
  88. //!
  89. //! - **http-all**: Enables all [HTTP APIs](https://tauri.app/en/docs/api/js/modules/http).
  90. //! - **http-request**: Enables the [`request` APIs](https://tauri.app/en/docs/api/js/classes/http.client/).
  91. //!
  92. //! ### Notification allowlist
  93. //!
  94. //! - **notification-all**: Enables all [Notification APIs](https://tauri.app/en/docs/api/js/modules/notification).
  95. //!
  96. //! ### OS allowlist
  97. //!
  98. //! - **os-all**: Enables all [OS APIs](https://tauri.app/en/docs/api/js/modules/os).
  99. //!
  100. //! ### Path allowlist
  101. //!
  102. //! - **path-all**: Enables all [Path APIs](https://tauri.app/en/docs/api/js/modules/path).
  103. //!
  104. //! ### Process allowlist
  105. //!
  106. //! - **process-all**: Enables all [Process APIs](https://tauri.app/en/docs/api/js/modules/process).
  107. //! - **process-exit**: Enables the [`exit` API](https://tauri.app/en/docs/api/js/modules/process#exit).
  108. //! - **process-relaunch**: Enables the [`relaunch` API](https://tauri.app/en/docs/api/js/modules/process#relaunch).
  109. //!
  110. //! ### Protocol allowlist
  111. //!
  112. //! - **protocol-all**: Enables all Protocol APIs.
  113. //! - **protocol-asset**: Enables the `asset` custom protocol.
  114. //!
  115. //! ### Shell allowlist
  116. //!
  117. //! - **shell-all**: Enables all [Clipboard APIs](https://tauri.app/en/docs/api/js/modules/shell).
  118. //! - **shell-execute**: Enables [executing arbitrary programs](https://tauri.app/en/docs/api/js/classes/shell.Command#constructor).
  119. //! - **shell-sidecar**: Enables [executing a `sidecar` program](https://tauri.app/en/docs/api/js/classes/shell.Command#sidecar).
  120. //! - **shell-open**: Enables the [`open` API](https://tauri.app/en/docs/api/js/modules/shell#open).
  121. //!
  122. //! ### Window allowlist
  123. //!
  124. //! - **window-all**: Enables all [Window APIs](https://tauri.app/en/docs/api/js/modules/window).
  125. //! - **window-create**: Enables the API used to [create new windows](https://tauri.app/en/docs/api/js/classes/window.webviewwindow/).
  126. //! - **window-center**: Enables the [`center` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#center).
  127. //! - **window-request-user-attention**: Enables the [`requestUserAttention` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#requestuserattention).
  128. //! - **window-set-resizable**: Enables the [`setResizable` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setresizable).
  129. //! - **window-set-maximizable**: Enables the [`setMaximizable` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setmaximizable).
  130. //! - **window-set-minimizable**: Enables the [`setMinimizable` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setminimizable).
  131. //! - **window-set-closable**: Enables the [`setClosable` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setclosable).
  132. //! - **window-set-title**: Enables the [`setTitle` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#settitle).
  133. //! - **window-maximize**: Enables the [`maximize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#maximize).
  134. //! - **window-unmaximize**: Enables the [`unmaximize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#unmaximize).
  135. //! - **window-minimize**: Enables the [`minimize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#minimize).
  136. //! - **window-unminimize**: Enables the [`unminimize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#unminimize).
  137. //! - **window-show**: Enables the [`show` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#show).
  138. //! - **window-hide**: Enables the [`hide` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#hide).
  139. //! - **window-close**: Enables the [`close` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#close).
  140. //! - **window-set-decorations**: Enables the [`setDecorations` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setdecorations).
  141. //! - **window-set-always-on-top**: Enables the [`setAlwaysOnTop` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setalwaysontop).
  142. //! - **window-set-content-protected**: Enables the [`setContentProtected` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setcontentprotected).
  143. //! - **window-set-size**: Enables the [`setSize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setsize).
  144. //! - **window-set-min-size**: Enables the [`setMinSize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setminsize).
  145. //! - **window-set-max-size**: Enables the [`setMaxSize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setmaxsize).
  146. //! - **window-set-position**: Enables the [`setPosition` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setposition).
  147. //! - **window-set-fullscreen**: Enables the [`setFullscreen` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setfullscreen).
  148. //! - **window-set-focus**: Enables the [`setFocus` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setfocus).
  149. //! - **window-set-icon**: Enables the [`setIcon` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#seticon).
  150. //! - **window-set-skip-taskbar**: Enables the [`setSkipTaskbar` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setskiptaskbar).
  151. //! - **window-set-cursor-grab**: Enables the [`setCursorGrab` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setcursorgrab).
  152. //! - **window-set-cursor-visible**: Enables the [`setCursorVisible` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setcursorvisible).
  153. //! - **window-set-cursor-icon**: Enables the [`setCursorIcon` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setcursoricon).
  154. //! - **window-set-cursor-position**: Enables the [`setCursorPosition` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setcursorposition).
  155. //! - **window-set-ignore-cursor-events**: Enables the [`setIgnoreCursorEvents` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setignorecursorevents).
  156. //! - **window-start-dragging**: Enables the [`startDragging` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#startdragging).
  157. //! - **window-print**: Enables the [`print` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#print).
  158. //!
  159. //! ### App allowlist
  160. //!
  161. //! - **app-all**: Enables all [App APIs](https://tauri.app/en/docs/api/js/modules/app).
  162. //! - **app-show**: Enables the [`show` API](https://tauri.app/en/docs/api/js/modules/app#show).
  163. //! - **app-hide**: Enables the [`hide` API](https://tauri.app/en/docs/api/js/modules/app#hide).
  164. #![warn(missing_docs, rust_2018_idioms)]
  165. #![cfg_attr(doc_cfg, feature(doc_cfg))]
  166. #[cfg(target_os = "macos")]
  167. #[doc(hidden)]
  168. pub use embed_plist;
  169. /// The Tauri error enum.
  170. pub use error::Error;
  171. #[cfg(shell_scope)]
  172. #[doc(hidden)]
  173. pub use regex;
  174. pub use tauri_macros::{command, generate_handler};
  175. pub use url::Url;
  176. pub mod api;
  177. pub(crate) mod app;
  178. pub mod async_runtime;
  179. pub mod command;
  180. /// The Tauri API endpoints.
  181. mod endpoints;
  182. mod error;
  183. mod event;
  184. mod hooks;
  185. mod manager;
  186. mod pattern;
  187. pub mod plugin;
  188. pub mod window;
  189. use tauri_runtime as runtime;
  190. #[cfg(protocol_asset)]
  191. mod asset_protocol;
  192. /// The allowlist scopes.
  193. pub mod scope;
  194. mod state;
  195. #[cfg(updater)]
  196. #[cfg_attr(doc_cfg, doc(cfg(feature = "updater")))]
  197. pub mod updater;
  198. pub use tauri_utils as utils;
  199. /// A Tauri [`Runtime`] wrapper around wry.
  200. #[cfg(feature = "wry")]
  201. #[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))]
  202. pub type Wry = tauri_runtime_wry::Wry<EventLoopMessage>;
  203. /// `Result<T, ::tauri::Error>`
  204. pub type Result<T> = std::result::Result<T, Error>;
  205. /// A task to run on the main thread.
  206. pub type SyncTask = Box<dyn FnOnce() + Send>;
  207. use serde::Serialize;
  208. use std::{collections::HashMap, fmt, sync::Arc};
  209. // Export types likely to be used by the application.
  210. pub use runtime::http;
  211. #[cfg(feature = "wry")]
  212. #[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))]
  213. pub use tauri_runtime_wry::webview_version;
  214. #[cfg(target_os = "macos")]
  215. #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
  216. pub use runtime::{menu::NativeImage, ActivationPolicy};
  217. #[cfg(target_os = "macos")]
  218. pub use self::utils::TitleBarStyle;
  219. #[cfg(all(desktop, feature = "system-tray"))]
  220. #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
  221. pub use {
  222. self::app::tray::{SystemTray, SystemTrayEvent, SystemTrayHandle, SystemTrayMenuItemHandle},
  223. self::runtime::menu::{SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu},
  224. };
  225. pub use {
  226. self::app::WindowMenuEvent,
  227. self::event::{Event, EventHandler},
  228. self::runtime::menu::{AboutMetadata, CustomMenuItem, Menu, MenuEntry, MenuItem, Submenu},
  229. self::window::menu::MenuEvent,
  230. };
  231. pub use {
  232. self::app::{
  233. App, AppHandle, AssetResolver, Builder, CloseRequestApi, GlobalWindowEvent, PathResolver,
  234. RunEvent, WindowEvent,
  235. },
  236. self::hooks::{
  237. Invoke, InvokeError, InvokeHandler, InvokeMessage, InvokePayload, InvokeResolver,
  238. InvokeResponder, InvokeResponse, OnPageLoad, PageLoadPayload, SetupHook,
  239. },
  240. self::manager::Asset,
  241. self::runtime::{
  242. webview::WebviewAttributes,
  243. window::{
  244. dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
  245. CursorIcon, FileDropEvent,
  246. },
  247. DeviceEventFilter, RunIteration, UserAttentionType,
  248. },
  249. self::state::{State, StateManager},
  250. self::utils::{
  251. assets::Assets,
  252. config::{Config, WindowUrl},
  253. Env, PackageInfo, Theme,
  254. },
  255. self::window::{Monitor, Window, WindowBuilder},
  256. scope::*,
  257. };
  258. #[cfg(feature = "clipboard")]
  259. #[cfg_attr(doc_cfg, doc(cfg(feature = "clipboard")))]
  260. pub use self::runtime::ClipboardManager;
  261. #[cfg(all(desktop, feature = "global-shortcut"))]
  262. #[cfg_attr(doc_cfg, doc(cfg(feature = "global-shortcut")))]
  263. pub use self::runtime::GlobalShortcutManager;
  264. /// The Tauri version.
  265. pub const VERSION: &str = env!("CARGO_PKG_VERSION");
  266. /// Updater events.
  267. #[cfg(updater)]
  268. #[cfg_attr(doc_cfg, doc(cfg(feature = "updater")))]
  269. #[derive(Debug, Clone)]
  270. pub enum UpdaterEvent {
  271. /// An update is available.
  272. UpdateAvailable {
  273. /// The update body.
  274. body: String,
  275. /// The update release date.
  276. date: Option<time::OffsetDateTime>,
  277. /// The update version.
  278. version: String,
  279. },
  280. /// The update is pending and about to be downloaded.
  281. Pending,
  282. /// The update download received a progress event.
  283. DownloadProgress {
  284. /// The amount that was downloaded on this iteration.
  285. /// Does not accumulate with previous chunks.
  286. chunk_length: usize,
  287. /// The total
  288. content_length: Option<u64>,
  289. },
  290. /// The update has been downloaded and is now about to be installed.
  291. Downloaded,
  292. /// The update has been applied and the app is now up to date.
  293. Updated,
  294. /// The app is already up to date.
  295. AlreadyUpToDate,
  296. /// An error occurred while updating.
  297. Error(String),
  298. }
  299. #[cfg(updater)]
  300. impl UpdaterEvent {
  301. pub(crate) fn status_message(self) -> &'static str {
  302. match self {
  303. Self::Pending => updater::EVENT_STATUS_PENDING,
  304. Self::Downloaded => updater::EVENT_STATUS_DOWNLOADED,
  305. Self::Updated => updater::EVENT_STATUS_SUCCESS,
  306. Self::AlreadyUpToDate => updater::EVENT_STATUS_UPTODATE,
  307. Self::Error(_) => updater::EVENT_STATUS_ERROR,
  308. _ => unreachable!(),
  309. }
  310. }
  311. }
  312. /// The user event type.
  313. #[derive(Debug, Clone)]
  314. pub enum EventLoopMessage {
  315. /// Updater event.
  316. #[cfg(updater)]
  317. #[cfg_attr(doc_cfg, doc(cfg(feature = "updater")))]
  318. Updater(UpdaterEvent),
  319. }
  320. /// The webview runtime interface. A wrapper around [`runtime::Runtime`] with the proper user event type associated.
  321. pub trait Runtime: runtime::Runtime<EventLoopMessage> {}
  322. impl<W: runtime::Runtime<EventLoopMessage>> Runtime for W {}
  323. /// Reads the config file at compile time and generates a [`Context`] based on its content.
  324. ///
  325. /// The default config file path is a `tauri.conf.json` file inside the Cargo manifest directory of
  326. /// the crate being built.
  327. ///
  328. /// # Custom Config Path
  329. ///
  330. /// You may pass a string literal to this macro to specify a custom path for the Tauri config file.
  331. /// If the path is relative, it will be search for relative to the Cargo manifest of the compiling
  332. /// crate.
  333. ///
  334. /// # Note
  335. ///
  336. /// This macro should not be called if you are using [`tauri-build`] to generate the context from
  337. /// inside your build script as it will just cause excess computations that will be discarded. Use
  338. /// either the [`tauri-build`] method or this macro - not both.
  339. ///
  340. /// [`tauri-build`]: https://docs.rs/tauri-build
  341. pub use tauri_macros::generate_context;
  342. /// Include a [`Context`] that was generated by [`tauri-build`] inside your build script.
  343. ///
  344. /// You should either use [`tauri-build`] and this macro to include the compile time generated code,
  345. /// or [`generate_context!`]. Do not use both at the same time, as they generate the same code and
  346. /// will cause excess computations that will be discarded.
  347. ///
  348. /// [`tauri-build`]: https://docs.rs/tauri-build
  349. #[macro_export]
  350. macro_rules! tauri_build_context {
  351. () => {
  352. include!(concat!(env!("OUT_DIR"), "/tauri-build-context.rs"))
  353. };
  354. }
  355. pub use pattern::Pattern;
  356. /// A icon definition.
  357. #[derive(Debug, Clone)]
  358. #[non_exhaustive]
  359. pub enum Icon {
  360. /// Icon from file path.
  361. #[cfg(any(feature = "icon-ico", feature = "icon-png"))]
  362. #[cfg_attr(doc_cfg, doc(cfg(any(feature = "icon-ico", feature = "icon-png"))))]
  363. File(std::path::PathBuf),
  364. /// Icon from raw RGBA bytes. Width and height is parsed at runtime.
  365. #[cfg(any(feature = "icon-ico", feature = "icon-png"))]
  366. #[cfg_attr(doc_cfg, doc(cfg(any(feature = "icon-ico", feature = "icon-png"))))]
  367. Raw(Vec<u8>),
  368. /// Icon from raw RGBA bytes.
  369. Rgba {
  370. /// RGBA bytes of the icon image.
  371. rgba: Vec<u8>,
  372. /// Icon width.
  373. width: u32,
  374. /// Icon height.
  375. height: u32,
  376. },
  377. }
  378. impl TryFrom<Icon> for runtime::Icon {
  379. type Error = Error;
  380. fn try_from(icon: Icon) -> Result<Self> {
  381. #[allow(irrefutable_let_patterns)]
  382. if let Icon::Rgba {
  383. rgba,
  384. width,
  385. height,
  386. } = icon
  387. {
  388. Ok(Self {
  389. rgba,
  390. width,
  391. height,
  392. })
  393. } else {
  394. #[cfg(not(any(feature = "icon-ico", feature = "icon-png")))]
  395. panic!("unexpected Icon variant");
  396. #[cfg(any(feature = "icon-ico", feature = "icon-png"))]
  397. {
  398. let bytes = match icon {
  399. Icon::File(p) => std::fs::read(p)?,
  400. Icon::Raw(r) => r,
  401. Icon::Rgba { .. } => unreachable!(),
  402. };
  403. let extension = infer::get(&bytes)
  404. .expect("could not determine icon extension")
  405. .extension();
  406. match extension {
  407. #[cfg(feature = "icon-ico")]
  408. "ico" => {
  409. let icon_dir = ico::IconDir::read(std::io::Cursor::new(bytes))?;
  410. let entry = &icon_dir.entries()[0];
  411. Ok(Self {
  412. rgba: entry.decode()?.rgba_data().to_vec(),
  413. width: entry.width(),
  414. height: entry.height(),
  415. })
  416. }
  417. #[cfg(feature = "icon-png")]
  418. "png" => {
  419. let decoder = png::Decoder::new(std::io::Cursor::new(bytes));
  420. let mut reader = decoder.read_info()?;
  421. let mut buffer = Vec::new();
  422. while let Ok(Some(row)) = reader.next_row() {
  423. buffer.extend(row.data());
  424. }
  425. Ok(Self {
  426. rgba: buffer,
  427. width: reader.info().width,
  428. height: reader.info().height,
  429. })
  430. }
  431. _ => panic!(
  432. "image `{extension}` extension not supported; please file a Tauri feature request. `png` or `ico` icons are supported with the `icon-png` and `icon-ico` feature flags"
  433. ),
  434. }
  435. }
  436. }
  437. }
  438. }
  439. /// User supplied data required inside of a Tauri application.
  440. ///
  441. /// # Stability
  442. /// This is the output of the [`generate_context`] macro, and is not considered part of the stable API.
  443. /// Unless you know what you are doing and are prepared for this type to have breaking changes, do not create it yourself.
  444. pub struct Context<A: Assets> {
  445. pub(crate) config: Config,
  446. pub(crate) assets: Arc<A>,
  447. pub(crate) default_window_icon: Option<Icon>,
  448. pub(crate) app_icon: Option<Vec<u8>>,
  449. pub(crate) system_tray_icon: Option<Icon>,
  450. pub(crate) package_info: PackageInfo,
  451. pub(crate) _info_plist: (),
  452. pub(crate) pattern: Pattern,
  453. #[cfg(shell_scope)]
  454. pub(crate) shell_scope: scope::ShellScopeConfig,
  455. }
  456. impl<A: Assets> fmt::Debug for Context<A> {
  457. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  458. let mut d = f.debug_struct("Context");
  459. d.field("config", &self.config)
  460. .field("default_window_icon", &self.default_window_icon)
  461. .field("app_icon", &self.app_icon)
  462. .field("system_tray_icon", &self.system_tray_icon)
  463. .field("package_info", &self.package_info)
  464. .field("pattern", &self.pattern);
  465. #[cfg(shell_scope)]
  466. d.field("shell_scope", &self.shell_scope);
  467. d.finish()
  468. }
  469. }
  470. impl<A: Assets> Context<A> {
  471. /// The config the application was prepared with.
  472. #[inline(always)]
  473. pub fn config(&self) -> &Config {
  474. &self.config
  475. }
  476. /// A mutable reference to the config the application was prepared with.
  477. #[inline(always)]
  478. pub fn config_mut(&mut self) -> &mut Config {
  479. &mut self.config
  480. }
  481. /// The assets to be served directly by Tauri.
  482. #[inline(always)]
  483. pub fn assets(&self) -> Arc<A> {
  484. self.assets.clone()
  485. }
  486. /// A mutable reference to the assets to be served directly by Tauri.
  487. #[inline(always)]
  488. pub fn assets_mut(&mut self) -> &mut Arc<A> {
  489. &mut self.assets
  490. }
  491. /// The default window icon Tauri should use when creating windows.
  492. #[inline(always)]
  493. pub fn default_window_icon(&self) -> Option<&Icon> {
  494. self.default_window_icon.as_ref()
  495. }
  496. /// A mutable reference to the default window icon Tauri should use when creating windows.
  497. #[inline(always)]
  498. pub fn default_window_icon_mut(&mut self) -> &mut Option<Icon> {
  499. &mut self.default_window_icon
  500. }
  501. /// The icon to use on the system tray UI.
  502. #[inline(always)]
  503. pub fn system_tray_icon(&self) -> Option<&Icon> {
  504. self.system_tray_icon.as_ref()
  505. }
  506. /// A mutable reference to the icon to use on the system tray UI.
  507. #[inline(always)]
  508. pub fn system_tray_icon_mut(&mut self) -> &mut Option<Icon> {
  509. &mut self.system_tray_icon
  510. }
  511. /// Package information.
  512. #[inline(always)]
  513. pub fn package_info(&self) -> &PackageInfo {
  514. &self.package_info
  515. }
  516. /// A mutable reference to the package information.
  517. #[inline(always)]
  518. pub fn package_info_mut(&mut self) -> &mut PackageInfo {
  519. &mut self.package_info
  520. }
  521. /// The application pattern.
  522. #[inline(always)]
  523. pub fn pattern(&self) -> &Pattern {
  524. &self.pattern
  525. }
  526. /// The scoped shell commands, where the `HashMap` key is the name each configuration.
  527. #[cfg(shell_scope)]
  528. #[inline(always)]
  529. pub fn allowed_commands(&self) -> &scope::ShellScopeConfig {
  530. &self.shell_scope
  531. }
  532. /// Create a new [`Context`] from the minimal required items.
  533. #[inline(always)]
  534. #[allow(clippy::too_many_arguments)]
  535. pub fn new(
  536. config: Config,
  537. assets: Arc<A>,
  538. default_window_icon: Option<Icon>,
  539. app_icon: Option<Vec<u8>>,
  540. system_tray_icon: Option<Icon>,
  541. package_info: PackageInfo,
  542. info_plist: (),
  543. pattern: Pattern,
  544. #[cfg(shell_scope)] shell_scope: scope::ShellScopeConfig,
  545. ) -> Self {
  546. Self {
  547. config,
  548. assets,
  549. default_window_icon,
  550. app_icon,
  551. system_tray_icon,
  552. package_info,
  553. _info_plist: info_plist,
  554. pattern,
  555. #[cfg(shell_scope)]
  556. shell_scope,
  557. }
  558. }
  559. }
  560. // TODO: expand these docs
  561. /// Manages a running application.
  562. pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
  563. /// The application handle associated with this manager.
  564. fn app_handle(&self) -> AppHandle<R> {
  565. self.managed_app_handle()
  566. }
  567. /// The [`Config`] the manager was created with.
  568. fn config(&self) -> Arc<Config> {
  569. self.manager().config()
  570. }
  571. /// Emits an event to all windows.
  572. ///
  573. /// Only the webviews receives this event.
  574. /// To trigger Rust listeners, use [`Self::trigger_global`], [`Window::trigger`] or [`Window::emit_and_trigger`].
  575. ///
  576. /// # Examples
  577. /// ```
  578. /// use tauri::Manager;
  579. ///
  580. /// #[tauri::command]
  581. /// fn synchronize(app: tauri::AppHandle) {
  582. /// // emits the synchronized event to all windows
  583. /// app.emit_all("synchronized", ());
  584. /// }
  585. /// ```
  586. #[cfg_attr(
  587. feature = "tracing",
  588. tracing::instrument("app::emit::all", skip(self, payload))
  589. )]
  590. fn emit_all<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
  591. self.manager().emit_filter(event, None, payload, |_| true)
  592. }
  593. /// Emits an event to windows matching the filter critera.
  594. ///
  595. /// # Examples
  596. /// ```
  597. /// use tauri::Manager;
  598. ///
  599. /// #[tauri::command]
  600. /// fn synchronize(app: tauri::AppHandle) {
  601. /// // emits the synchronized event to all windows
  602. /// app.emit_filter("synchronized", (), |w| w.label().starts_with("foo-"));
  603. /// }
  604. /// ```
  605. #[cfg_attr(
  606. feature = "tracing",
  607. tracing::instrument("app::emit::filter", skip(self, payload, filter))
  608. )]
  609. fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
  610. where
  611. S: Serialize + Clone,
  612. F: Fn(&Window<R>) -> bool,
  613. {
  614. self.manager().emit_filter(event, None, payload, filter)
  615. }
  616. /// Emits an event to the window with the specified label.
  617. ///
  618. /// # Examples
  619. /// ```
  620. /// use tauri::Manager;
  621. ///
  622. /// #[tauri::command]
  623. /// fn download(app: tauri::AppHandle) {
  624. /// for i in 1..100 {
  625. /// std::thread::sleep(std::time::Duration::from_millis(150));
  626. /// // emit a download progress event to the updater window
  627. /// app.emit_to("updater", "download-progress", i);
  628. /// }
  629. /// }
  630. /// ```
  631. #[cfg_attr(
  632. feature = "tracing",
  633. tracing::instrument("app::emit::to", skip(self, payload))
  634. )]
  635. fn emit_to<S: Serialize + Clone>(&self, label: &str, event: &str, payload: S) -> Result<()> {
  636. self
  637. .manager()
  638. .emit_filter(event, None, payload, |w| label == w.label())
  639. }
  640. /// Listen to a event triggered on any window ([`Window::trigger`] or [`Window::emit_and_trigger`]) or with [`Self::trigger_global`].
  641. ///
  642. /// # Examples
  643. /// ```
  644. /// use tauri::Manager;
  645. ///
  646. /// #[tauri::command]
  647. /// fn synchronize(window: tauri::Window) {
  648. /// // emits the synchronized event to all windows
  649. /// window.emit_and_trigger("synchronized", ());
  650. /// }
  651. ///
  652. /// tauri::Builder::default()
  653. /// .setup(|app| {
  654. /// app.listen_global("synchronized", |event| {
  655. /// println!("app is in sync");
  656. /// });
  657. /// Ok(())
  658. /// })
  659. /// .invoke_handler(tauri::generate_handler![synchronize]);
  660. /// ```
  661. fn listen_global<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
  662. where
  663. F: Fn(Event) + Send + 'static,
  664. {
  665. self.manager().listen(event.into(), None, handler)
  666. }
  667. /// Listen to a global event only once.
  668. ///
  669. /// See [`Self::listen_global`] for more information.
  670. fn once_global<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
  671. where
  672. F: FnOnce(Event) + Send + 'static,
  673. {
  674. self.manager().once(event.into(), None, handler)
  675. }
  676. /// Trigger a global event to Rust listeners.
  677. /// To send the events to the webview, see [`Self::emit_all`] and [`Self::emit_to`].
  678. /// To trigger listeners registed on an specific window, see [`Window::trigger`].
  679. /// To trigger all listeners, see [`Window::emit_and_trigger`].
  680. ///
  681. /// A global event does not have a source or target window attached.
  682. ///
  683. /// # Examples
  684. /// ```
  685. /// use tauri::Manager;
  686. ///
  687. /// #[tauri::command]
  688. /// fn download(app: tauri::AppHandle) {
  689. /// for i in 1..100 {
  690. /// std::thread::sleep(std::time::Duration::from_millis(150));
  691. /// // emit a download progress event to all listeners registed in Rust
  692. /// app.trigger_global("download-progress", Some(i.to_string()));
  693. /// }
  694. /// }
  695. /// ```
  696. #[cfg_attr(
  697. feature = "tracing",
  698. tracing::instrument("app::emit::rust", skip(self))
  699. )]
  700. fn trigger_global(&self, event: &str, data: Option<String>) {
  701. self.manager().trigger(event, None, data)
  702. }
  703. /// Remove an event listener.
  704. ///
  705. /// # Examples
  706. /// ```
  707. /// use tauri::Manager;
  708. ///
  709. /// tauri::Builder::default()
  710. /// .setup(|app| {
  711. /// let handle = app.handle();
  712. /// let handler = app.listen_global("ready", move |event| {
  713. /// println!("app is ready");
  714. ///
  715. /// // we no longer need to listen to the event
  716. /// // we also could have used `app.once_global` instead
  717. /// handle.unlisten(event.id());
  718. /// });
  719. ///
  720. /// // stop listening to the event when you do not need it anymore
  721. /// app.unlisten(handler);
  722. ///
  723. ///
  724. /// Ok(())
  725. /// });
  726. /// ```
  727. fn unlisten(&self, handler_id: EventHandler) {
  728. self.manager().unlisten(handler_id)
  729. }
  730. /// Fetch a single window from the manager.
  731. fn get_window(&self, label: &str) -> Option<Window<R>> {
  732. self.manager().get_window(label)
  733. }
  734. /// Fetch the focused window. Returns `None` if there is not any focused window.
  735. fn get_focused_window(&self) -> Option<Window<R>> {
  736. self.manager().get_focused_window()
  737. }
  738. /// Fetch all managed windows.
  739. fn windows(&self) -> HashMap<String, Window<R>> {
  740. self.manager().windows()
  741. }
  742. /// Add `state` to the state managed by the application.
  743. ///
  744. /// If the state for the `T` type has previously been set, the state is unchanged and false is returned. Otherwise true is returned.
  745. ///
  746. /// Managed state can be retrieved by any command handler via the
  747. /// [`State`](crate::State) guard. In particular, if a value of type `T`
  748. /// is managed by Tauri, adding `State<T>` to the list of arguments in a
  749. /// command handler instructs Tauri to retrieve the managed value.
  750. /// Additionally, [`state`](Self#method.state) can be used to retrieve the value manually.
  751. ///
  752. /// # Mutability
  753. ///
  754. /// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
  755. ///
  756. /// ```rust,no_run
  757. /// use std::{collections::HashMap, sync::Mutex};
  758. /// use tauri::State;
  759. /// // here we use Mutex to achieve interior mutability
  760. /// struct Storage {
  761. /// store: Mutex<HashMap<u64, String>>,
  762. /// }
  763. /// struct Connection;
  764. /// struct DbConnection {
  765. /// db: Mutex<Option<Connection>>,
  766. /// }
  767. ///
  768. /// #[tauri::command]
  769. /// fn connect(connection: State<DbConnection>) {
  770. /// // initialize the connection, mutating the state with interior mutability
  771. /// *connection.db.lock().unwrap() = Some(Connection {});
  772. /// }
  773. ///
  774. /// #[tauri::command]
  775. /// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
  776. /// // mutate the storage behind the Mutex
  777. /// storage.store.lock().unwrap().insert(key, value);
  778. /// }
  779. ///
  780. /// tauri::Builder::default()
  781. /// .manage(Storage { store: Default::default() })
  782. /// .manage(DbConnection { db: Default::default() })
  783. /// .invoke_handler(tauri::generate_handler![connect, storage_insert])
  784. /// // on an actual app, remove the string argument
  785. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  786. /// .expect("error while running tauri application");
  787. /// ```
  788. ///
  789. /// # Examples
  790. ///
  791. /// ```rust,no_run
  792. /// use tauri::{Manager, State};
  793. ///
  794. /// struct MyInt(isize);
  795. /// struct MyString(String);
  796. ///
  797. /// #[tauri::command]
  798. /// fn int_command(state: State<MyInt>) -> String {
  799. /// format!("The stateful int is: {}", state.0)
  800. /// }
  801. ///
  802. /// #[tauri::command]
  803. /// fn string_command<'r>(state: State<'r, MyString>) {
  804. /// println!("state: {}", state.inner().0);
  805. /// }
  806. ///
  807. /// tauri::Builder::default()
  808. /// .setup(|app| {
  809. /// app.manage(MyInt(0));
  810. /// app.manage(MyString("tauri".into()));
  811. /// // `MyInt` is already managed, so `manage()` returns false
  812. /// assert!(!app.manage(MyInt(1)));
  813. /// // read the `MyInt` managed state with the turbofish syntax
  814. /// let int = app.state::<MyInt>();
  815. /// assert_eq!(int.0, 0);
  816. /// // read the `MyString` managed state with the `State` guard
  817. /// let val: State<MyString> = app.state();
  818. /// assert_eq!(val.0, "tauri");
  819. /// Ok(())
  820. /// })
  821. /// .invoke_handler(tauri::generate_handler![int_command, string_command])
  822. /// // on an actual app, remove the string argument
  823. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  824. /// .expect("error while running tauri application");
  825. /// ```
  826. fn manage<T>(&self, state: T) -> bool
  827. where
  828. T: Send + Sync + 'static,
  829. {
  830. self.manager().state().set(state)
  831. }
  832. /// Retrieves the managed state for the type `T`.
  833. ///
  834. /// # Panics
  835. ///
  836. /// Panics if the state for the type `T` has not been previously [managed](Self::manage).
  837. /// Use [try_state](Self::try_state) for a non-panicking version.
  838. fn state<T>(&self) -> State<'_, T>
  839. where
  840. T: Send + Sync + 'static,
  841. {
  842. self
  843. .manager()
  844. .inner
  845. .state
  846. .try_get()
  847. .expect("state() called before manage() for given type")
  848. }
  849. /// Attempts to retrieve the managed state for the type `T`.
  850. ///
  851. /// Returns `Some` if the state has previously been [managed](Self::manage). Otherwise returns `None`.
  852. fn try_state<T>(&self) -> Option<State<'_, T>>
  853. where
  854. T: Send + Sync + 'static,
  855. {
  856. self.manager().inner.state.try_get()
  857. }
  858. /// Gets the managed [`Env`].
  859. fn env(&self) -> Env {
  860. self.state::<Env>().inner().clone()
  861. }
  862. /// Gets the scope for the filesystem APIs.
  863. fn fs_scope(&self) -> FsScope {
  864. self.state::<Scopes>().inner().fs.clone()
  865. }
  866. /// Gets the scope for the IPC.
  867. fn ipc_scope(&self) -> IpcScope {
  868. self.state::<Scopes>().inner().ipc.clone()
  869. }
  870. /// Gets the scope for the asset protocol.
  871. #[cfg(protocol_asset)]
  872. fn asset_protocol_scope(&self) -> FsScope {
  873. self.state::<Scopes>().inner().asset_protocol.clone()
  874. }
  875. /// Gets the scope for the shell execute APIs.
  876. #[cfg(shell_scope)]
  877. fn shell_scope(&self) -> ShellScope {
  878. self.state::<Scopes>().inner().shell.clone()
  879. }
  880. }
  881. /// Prevent implementation details from leaking out of the [`Manager`] trait.
  882. pub(crate) mod sealed {
  883. use super::Runtime;
  884. use crate::{app::AppHandle, manager::WindowManager};
  885. /// A running [`Runtime`] or a dispatcher to it.
  886. pub enum RuntimeOrDispatch<'r, R: Runtime> {
  887. /// Reference to the running [`Runtime`].
  888. Runtime(&'r R),
  889. /// Handle to the running [`Runtime`].
  890. RuntimeHandle(R::Handle),
  891. /// A dispatcher to the running [`Runtime`].
  892. Dispatch(R::Dispatcher),
  893. }
  894. /// Managed handle to the application runtime.
  895. pub trait ManagerBase<R: Runtime> {
  896. /// The manager behind the [`Managed`] item.
  897. fn manager(&self) -> &WindowManager<R>;
  898. fn runtime(&self) -> RuntimeOrDispatch<'_, R>;
  899. fn managed_app_handle(&self) -> AppHandle<R>;
  900. }
  901. }
  902. #[cfg(any(test, feature = "test"))]
  903. #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))]
  904. pub mod test;
  905. #[cfg(test)]
  906. mod tests {
  907. use cargo_toml::Manifest;
  908. use once_cell::sync::OnceCell;
  909. use std::{env::var, fs::read_to_string, path::PathBuf};
  910. static MANIFEST: OnceCell<Manifest> = OnceCell::new();
  911. const CHECKED_FEATURES: &str = include_str!(concat!(env!("OUT_DIR"), "/checked_features"));
  912. fn get_manifest() -> &'static Manifest {
  913. MANIFEST.get_or_init(|| {
  914. let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
  915. Manifest::from_path(manifest_dir.join("Cargo.toml")).expect("failed to parse Cargo manifest")
  916. })
  917. }
  918. #[test]
  919. fn features_are_documented() {
  920. let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
  921. let lib_code = read_to_string(manifest_dir.join("src/lib.rs")).expect("failed to read lib.rs");
  922. for f in get_manifest().features.keys() {
  923. if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{f}**"))) {
  924. panic!("Feature {f} is not documented");
  925. }
  926. }
  927. }
  928. #[test]
  929. fn aliased_features_exist() {
  930. let checked_features = CHECKED_FEATURES.split(',');
  931. let manifest = get_manifest();
  932. for checked_feature in checked_features {
  933. if !manifest.features.iter().any(|(f, _)| f == checked_feature) {
  934. panic!(
  935. "Feature {checked_feature} was checked in the alias build step but it does not exist in core/tauri/Cargo.toml"
  936. );
  937. }
  938. }
  939. }
  940. #[test]
  941. fn all_allowlist_features_are_aliased() {
  942. let manifest = get_manifest();
  943. let all_modules = manifest
  944. .features
  945. .iter()
  946. .find(|(f, _)| f.as_str() == "api-all")
  947. .map(|(_, enabled)| enabled)
  948. .expect("api-all feature must exist");
  949. let checked_features = CHECKED_FEATURES.split(',').collect::<Vec<&str>>();
  950. assert!(
  951. checked_features.contains(&"api-all"),
  952. "`api-all` is not aliased"
  953. );
  954. // features that look like an allowlist feature, but are not
  955. let allowed = [
  956. "fs-extract-api",
  957. "http-api",
  958. "http-multipart",
  959. "os-api",
  960. "process-command-api",
  961. "process-relaunch-dangerous-allow-symlink-macos",
  962. "window-data-url",
  963. ];
  964. for module_all_feature in all_modules {
  965. let module = module_all_feature.replace("-all", "");
  966. assert!(
  967. checked_features.contains(&module_all_feature.as_str()),
  968. "`{module}` is not aliased"
  969. );
  970. let module_prefix = format!("{module}-");
  971. // we assume that module features are the ones that start with `<module>-`
  972. // though it's not 100% accurate, we have an allowed list to fix it
  973. let module_features = manifest
  974. .features
  975. .keys()
  976. .filter(|f| f.starts_with(&module_prefix));
  977. for module_feature in module_features {
  978. assert!(
  979. allowed.contains(&module_feature.as_str())
  980. || checked_features.contains(&module_feature.as_str()),
  981. "`{module_feature}` is not aliased"
  982. );
  983. }
  984. }
  985. }
  986. }
  987. #[cfg(test)]
  988. mod test_utils {
  989. use proptest::prelude::*;
  990. pub fn assert_send<T: Send>() {}
  991. pub fn assert_sync<T: Sync>() {}
  992. #[allow(dead_code)]
  993. pub fn assert_not_allowlist_error<T>(res: anyhow::Result<T>) {
  994. if let Err(e) = res {
  995. assert!(!e.to_string().contains("not on the allowlist"));
  996. }
  997. }
  998. proptest! {
  999. #![proptest_config(ProptestConfig::with_cases(10000))]
  1000. #[test]
  1001. // check to see if spawn executes a function.
  1002. fn check_spawn_task(task in "[a-z]+") {
  1003. // create dummy task function
  1004. let dummy_task = async move {
  1005. format!("{task}-run-dummy-task");
  1006. };
  1007. // call spawn
  1008. crate::async_runtime::spawn(dummy_task);
  1009. }
  1010. }
  1011. }