lib.rs 38 KB

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