lib.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. // Copyright 2019-2024 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! [![](https://github.com/tauri-apps/tauri/raw/dev/.github/splash.png)](https://tauri.app)
  5. //!
  6. //! This crate contains common code that is reused in many places and offers useful utilities like parsing configuration files, detecting platform triples, injecting the CSP, and managing assets.
  7. #![doc(
  8. html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
  9. html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
  10. )]
  11. #![warn(missing_docs, rust_2018_idioms)]
  12. #![allow(clippy::deprecated_semver)]
  13. use std::{
  14. ffi::OsString,
  15. fmt::Display,
  16. path::{Path, PathBuf},
  17. };
  18. use semver::Version;
  19. use serde::{Deserialize, Deserializer, Serialize, Serializer};
  20. pub mod acl;
  21. pub mod assets;
  22. pub mod config;
  23. pub mod html;
  24. pub mod io;
  25. pub mod mime_type;
  26. pub mod platform;
  27. pub mod plugin;
  28. /// Prepare application resources and sidecars.
  29. #[cfg(feature = "resources")]
  30. pub mod resources;
  31. #[cfg(feature = "build")]
  32. pub mod tokens;
  33. #[cfg(feature = "build")]
  34. pub mod build;
  35. /// Application pattern.
  36. pub mod pattern;
  37. /// `tauri::App` package information.
  38. #[derive(Debug, Clone)]
  39. pub struct PackageInfo {
  40. /// App name
  41. pub name: String,
  42. /// App version
  43. pub version: Version,
  44. /// The crate authors.
  45. pub authors: &'static str,
  46. /// The crate description.
  47. pub description: &'static str,
  48. /// The crate name.
  49. pub crate_name: &'static str,
  50. }
  51. #[allow(deprecated)]
  52. mod window_effects {
  53. use super::*;
  54. #[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize)]
  55. #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
  56. #[serde(rename_all = "camelCase")]
  57. /// Platform-specific window effects
  58. pub enum WindowEffect {
  59. /// A default material appropriate for the view's effectiveAppearance. **macOS 10.14-**
  60. #[deprecated(
  61. since = "macOS 10.14",
  62. note = "You should instead choose an appropriate semantic material."
  63. )]
  64. AppearanceBased,
  65. /// **macOS 10.14-**
  66. #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
  67. Light,
  68. /// **macOS 10.14-**
  69. #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
  70. Dark,
  71. /// **macOS 10.14-**
  72. #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
  73. MediumLight,
  74. /// **macOS 10.14-**
  75. #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
  76. UltraDark,
  77. /// **macOS 10.10+**
  78. Titlebar,
  79. /// **macOS 10.10+**
  80. Selection,
  81. /// **macOS 10.11+**
  82. Menu,
  83. /// **macOS 10.11+**
  84. Popover,
  85. /// **macOS 10.11+**
  86. Sidebar,
  87. /// **macOS 10.14+**
  88. HeaderView,
  89. /// **macOS 10.14+**
  90. Sheet,
  91. /// **macOS 10.14+**
  92. WindowBackground,
  93. /// **macOS 10.14+**
  94. HudWindow,
  95. /// **macOS 10.14+**
  96. FullScreenUI,
  97. /// **macOS 10.14+**
  98. Tooltip,
  99. /// **macOS 10.14+**
  100. ContentBackground,
  101. /// **macOS 10.14+**
  102. UnderWindowBackground,
  103. /// **macOS 10.14+**
  104. UnderPageBackground,
  105. /// Mica effect that matches the system dark perefence **Windows 11 Only**
  106. Mica,
  107. /// Mica effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**
  108. MicaDark,
  109. /// Mica effect with light mode **Windows 11 Only**
  110. MicaLight,
  111. /// Tabbed effect that matches the system dark perefence **Windows 11 Only**
  112. Tabbed,
  113. /// Tabbed effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**
  114. TabbedDark,
  115. /// Tabbed effect with light mode **Windows 11 Only**
  116. TabbedLight,
  117. /// **Windows 7/10/11(22H1) Only**
  118. ///
  119. /// ## Notes
  120. ///
  121. /// This effect has bad performance when resizing/dragging the window on Windows 11 build 22621.
  122. Blur,
  123. /// **Windows 10/11 Only**
  124. ///
  125. /// ## Notes
  126. ///
  127. /// This effect has bad performance when resizing/dragging the window on Windows 10 v1903+ and Windows 11 build 22000.
  128. Acrylic,
  129. }
  130. /// Window effect state **macOS only**
  131. ///
  132. /// <https://developer.apple.com/documentation/appkit/nsvisualeffectview/state>
  133. #[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize)]
  134. #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
  135. #[serde(rename_all = "camelCase")]
  136. pub enum WindowEffectState {
  137. /// Make window effect state follow the window's active state
  138. FollowsWindowActiveState,
  139. /// Make window effect state always active
  140. Active,
  141. /// Make window effect state always inactive
  142. Inactive,
  143. }
  144. }
  145. pub use window_effects::{WindowEffect, WindowEffectState};
  146. /// How the window title bar should be displayed on macOS.
  147. #[derive(Debug, Clone, PartialEq, Eq, Copy)]
  148. #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
  149. #[non_exhaustive]
  150. pub enum TitleBarStyle {
  151. /// A normal title bar.
  152. Visible,
  153. /// Makes the title bar transparent, so the window background color is shown instead.
  154. ///
  155. /// Useful if you don't need to have actual HTML under the title bar. This lets you avoid the caveats of using `TitleBarStyle::Overlay`. Will be more useful when Tauri lets you set a custom window background color.
  156. Transparent,
  157. /// Shows the title bar as a transparent overlay over the window's content.
  158. ///
  159. /// Keep in mind:
  160. /// - The height of the title bar is different on different OS versions, which can lead to window the controls and title not being where you don't expect.
  161. /// - You need to define a custom drag region to make your window draggable, however due to a limitation you can't drag the window when it's not in focus <https://github.com/tauri-apps/tauri/issues/4316>.
  162. /// - The color of the window title depends on the system theme.
  163. Overlay,
  164. }
  165. impl Default for TitleBarStyle {
  166. fn default() -> Self {
  167. Self::Visible
  168. }
  169. }
  170. impl Serialize for TitleBarStyle {
  171. fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
  172. where
  173. S: Serializer,
  174. {
  175. serializer.serialize_str(self.to_string().as_ref())
  176. }
  177. }
  178. impl<'de> Deserialize<'de> for TitleBarStyle {
  179. fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
  180. where
  181. D: Deserializer<'de>,
  182. {
  183. let s = String::deserialize(deserializer)?;
  184. Ok(match s.to_lowercase().as_str() {
  185. "transparent" => Self::Transparent,
  186. "overlay" => Self::Overlay,
  187. _ => Self::Visible,
  188. })
  189. }
  190. }
  191. impl Display for TitleBarStyle {
  192. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  193. write!(
  194. f,
  195. "{}",
  196. match self {
  197. Self::Visible => "Visible",
  198. Self::Transparent => "Transparent",
  199. Self::Overlay => "Overlay",
  200. }
  201. )
  202. }
  203. }
  204. /// System theme.
  205. #[derive(Debug, Copy, Clone, PartialEq, Eq)]
  206. #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
  207. #[non_exhaustive]
  208. pub enum Theme {
  209. /// Light theme.
  210. Light,
  211. /// Dark theme.
  212. Dark,
  213. }
  214. impl Serialize for Theme {
  215. fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
  216. where
  217. S: Serializer,
  218. {
  219. serializer.serialize_str(self.to_string().as_ref())
  220. }
  221. }
  222. impl<'de> Deserialize<'de> for Theme {
  223. fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
  224. where
  225. D: Deserializer<'de>,
  226. {
  227. let s = String::deserialize(deserializer)?;
  228. Ok(match s.to_lowercase().as_str() {
  229. "dark" => Self::Dark,
  230. _ => Self::Light,
  231. })
  232. }
  233. }
  234. impl Display for Theme {
  235. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  236. write!(
  237. f,
  238. "{}",
  239. match self {
  240. Self::Light => "light",
  241. Self::Dark => "dark",
  242. }
  243. )
  244. }
  245. }
  246. /// Information about environment variables.
  247. #[derive(Debug, Clone)]
  248. #[non_exhaustive]
  249. pub struct Env {
  250. /// The APPIMAGE environment variable.
  251. #[cfg(target_os = "linux")]
  252. pub appimage: Option<std::ffi::OsString>,
  253. /// The APPDIR environment variable.
  254. #[cfg(target_os = "linux")]
  255. pub appdir: Option<std::ffi::OsString>,
  256. /// The command line arguments of the current process.
  257. pub args_os: Vec<OsString>,
  258. }
  259. #[allow(clippy::derivable_impls)]
  260. impl Default for Env {
  261. fn default() -> Self {
  262. let args_os = std::env::args_os().collect();
  263. #[cfg(target_os = "linux")]
  264. {
  265. let env = Self {
  266. #[cfg(target_os = "linux")]
  267. appimage: std::env::var_os("APPIMAGE"),
  268. #[cfg(target_os = "linux")]
  269. appdir: std::env::var_os("APPDIR"),
  270. args_os,
  271. };
  272. if env.appimage.is_some() || env.appdir.is_some() {
  273. // validate that we're actually running on an AppImage
  274. // an AppImage is mounted to `/$TEMPDIR/.mount_${appPrefix}${hash}`
  275. // see <https://github.com/AppImage/AppImageKit/blob/1681fd84dbe09c7d9b22e13cdb16ea601aa0ec47/src/runtime.c#L501>
  276. // note that it is safe to use `std::env::current_exe` here since we just loaded an AppImage.
  277. let is_temp = std::env::current_exe()
  278. .map(|p| {
  279. p.display()
  280. .to_string()
  281. .starts_with(&format!("{}/.mount_", std::env::temp_dir().display()))
  282. })
  283. .unwrap_or(true);
  284. if !is_temp {
  285. log::warn!("`APPDIR` or `APPIMAGE` environment variable found but this application was not detected as an AppImage; this might be a security issue.");
  286. }
  287. }
  288. env
  289. }
  290. #[cfg(not(target_os = "linux"))]
  291. {
  292. Self { args_os }
  293. }
  294. }
  295. }
  296. /// The result type of `tauri-utils`.
  297. pub type Result<T> = std::result::Result<T, Error>;
  298. /// The error type of `tauri-utils`.
  299. #[derive(Debug, thiserror::Error)]
  300. #[non_exhaustive]
  301. pub enum Error {
  302. /// Target triple architecture error
  303. #[error("Unable to determine target-architecture")]
  304. Architecture,
  305. /// Target triple OS error
  306. #[error("Unable to determine target-os")]
  307. Os,
  308. /// Target triple environment error
  309. #[error("Unable to determine target-environment")]
  310. Environment,
  311. /// Tried to get resource on an unsupported platform
  312. #[error("Unsupported platform for reading resources")]
  313. UnsupportedPlatform,
  314. /// Get parent process error
  315. #[error("Could not get parent process")]
  316. ParentProcess,
  317. /// Get parent process PID error
  318. #[error("Could not get parent PID")]
  319. ParentPid,
  320. /// Get child process error
  321. #[error("Could not get child process")]
  322. ChildProcess,
  323. /// IO error
  324. #[error("{0}")]
  325. Io(#[from] std::io::Error),
  326. /// Invalid pattern.
  327. #[error("invalid pattern `{0}`. Expected either `brownfield` or `isolation`.")]
  328. InvalidPattern(String),
  329. /// Invalid glob pattern.
  330. #[cfg(feature = "resources")]
  331. #[error("{0}")]
  332. GlobPattern(#[from] glob::PatternError),
  333. /// Failed to use glob pattern.
  334. #[cfg(feature = "resources")]
  335. #[error("`{0}`")]
  336. Glob(#[from] glob::GlobError),
  337. /// Glob pattern did not find any results.
  338. #[cfg(feature = "resources")]
  339. #[error("glob pattern {0} path not found or didn't match any files.")]
  340. GlobPathNotFound(String),
  341. /// Error walking directory.
  342. #[cfg(feature = "resources")]
  343. #[error("{0}")]
  344. WalkdirError(#[from] walkdir::Error),
  345. /// Not allowed to walk dir.
  346. #[cfg(feature = "resources")]
  347. #[error("could not walk directory `{0}`, try changing `allow_walk` to true on the `ResourcePaths` constructor.")]
  348. NotAllowedToWalkDir(std::path::PathBuf),
  349. /// Resourece path doesn't exist
  350. #[cfg(feature = "resources")]
  351. #[error("resource path `{0}` doesn't exist")]
  352. ResourcePathNotFound(std::path::PathBuf),
  353. }
  354. /// Reconstructs a path from its components using the platform separator then converts it to String and removes UNC prefixes on Windows if it exists.
  355. pub fn display_path<P: AsRef<Path>>(p: P) -> String {
  356. dunce::simplified(&p.as_ref().components().collect::<PathBuf>())
  357. .display()
  358. .to_string()
  359. }
  360. /// Write the file only if the content of the existing file (if any) is different.
  361. ///
  362. /// This will always write unless the file exists with identical content.
  363. pub fn write_if_changed<P, C>(path: P, content: C) -> std::io::Result<()>
  364. where
  365. P: AsRef<Path>,
  366. C: AsRef<[u8]>,
  367. {
  368. if let Ok(existing) = std::fs::read(&path) {
  369. if existing == content.as_ref() {
  370. return Ok(());
  371. }
  372. }
  373. std::fs::write(path, content)
  374. }