lib.rs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! Tauri utility helpers
  5. #![warn(missing_docs, rust_2018_idioms)]
  6. use std::{
  7. fmt::Display,
  8. path::{Path, PathBuf},
  9. };
  10. use semver::Version;
  11. use serde::{Deserialize, Deserializer, Serialize, Serializer};
  12. use log::warn;
  13. pub mod assets;
  14. pub mod config;
  15. pub mod html;
  16. pub mod io;
  17. pub mod mime_type;
  18. pub mod platform;
  19. /// Prepare application resources and sidecars.
  20. #[cfg(feature = "resources")]
  21. pub mod resources;
  22. /// Application pattern.
  23. pub mod pattern;
  24. /// `tauri::App` package information.
  25. #[derive(Debug, Clone)]
  26. pub struct PackageInfo {
  27. /// App name
  28. pub name: String,
  29. /// App version
  30. pub version: Version,
  31. /// The crate authors.
  32. pub authors: &'static str,
  33. /// The crate description.
  34. pub description: &'static str,
  35. }
  36. impl PackageInfo {
  37. /// Returns the application package name.
  38. /// On macOS and Windows it's the `name` field, and on Linux it's the `name` in `kebab-case`.
  39. pub fn package_name(&self) -> String {
  40. #[cfg(target_os = "linux")]
  41. {
  42. use heck::ToKebabCase;
  43. self.name.clone().to_kebab_case()
  44. }
  45. #[cfg(not(target_os = "linux"))]
  46. self.name.clone()
  47. }
  48. }
  49. /// How the window title bar should be displayed on macOS.
  50. #[derive(Debug, Clone, PartialEq, Eq)]
  51. #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
  52. pub enum TitleBarStyle {
  53. /// A normal title bar.
  54. Visible,
  55. /// Makes the title bar transparent, so the window background color is shown instead.
  56. ///
  57. /// 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.
  58. Transparent,
  59. /// Shows the title bar as a transparent overlay over the window's content.
  60. ///
  61. /// Keep in mind:
  62. /// - 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.
  63. /// - 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>.
  64. /// - The color of the window title depends on the system theme.
  65. Overlay,
  66. }
  67. impl Default for TitleBarStyle {
  68. fn default() -> Self {
  69. Self::Visible
  70. }
  71. }
  72. impl Serialize for TitleBarStyle {
  73. fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
  74. where
  75. S: Serializer,
  76. {
  77. serializer.serialize_str(self.to_string().as_ref())
  78. }
  79. }
  80. impl<'de> Deserialize<'de> for TitleBarStyle {
  81. fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
  82. where
  83. D: Deserializer<'de>,
  84. {
  85. let s = String::deserialize(deserializer)?;
  86. Ok(match s.to_lowercase().as_str() {
  87. "transparent" => Self::Transparent,
  88. "overlay" => Self::Overlay,
  89. _ => Self::Visible,
  90. })
  91. }
  92. }
  93. impl Display for TitleBarStyle {
  94. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  95. write!(
  96. f,
  97. "{}",
  98. match self {
  99. Self::Visible => "Visible",
  100. Self::Transparent => "Transparent",
  101. Self::Overlay => "Overlay",
  102. }
  103. )
  104. }
  105. }
  106. /// System theme.
  107. #[derive(Debug, Copy, Clone, PartialEq, Eq)]
  108. #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
  109. #[non_exhaustive]
  110. pub enum Theme {
  111. /// Light theme.
  112. Light,
  113. /// Dark theme.
  114. Dark,
  115. }
  116. impl Serialize for Theme {
  117. fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
  118. where
  119. S: Serializer,
  120. {
  121. serializer.serialize_str(self.to_string().as_ref())
  122. }
  123. }
  124. impl<'de> Deserialize<'de> for Theme {
  125. fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
  126. where
  127. D: Deserializer<'de>,
  128. {
  129. let s = String::deserialize(deserializer)?;
  130. Ok(match s.to_lowercase().as_str() {
  131. "dark" => Self::Dark,
  132. _ => Self::Light,
  133. })
  134. }
  135. }
  136. impl Display for Theme {
  137. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  138. write!(
  139. f,
  140. "{}",
  141. match self {
  142. Self::Light => "light",
  143. Self::Dark => "dark",
  144. }
  145. )
  146. }
  147. }
  148. /// Information about environment variables.
  149. #[derive(Debug, Clone)]
  150. #[non_exhaustive]
  151. pub struct Env {
  152. /// The APPIMAGE environment variable.
  153. #[cfg(target_os = "linux")]
  154. pub appimage: Option<std::ffi::OsString>,
  155. /// The APPDIR environment variable.
  156. #[cfg(target_os = "linux")]
  157. pub appdir: Option<std::ffi::OsString>,
  158. /// The command line arguments of the current process.
  159. pub args: Vec<String>,
  160. }
  161. #[allow(clippy::derivable_impls)]
  162. impl Default for Env {
  163. fn default() -> Self {
  164. let args = std::env::args().skip(1).collect();
  165. #[cfg(target_os = "linux")]
  166. {
  167. let env = Self {
  168. #[cfg(target_os = "linux")]
  169. appimage: std::env::var_os("APPIMAGE"),
  170. #[cfg(target_os = "linux")]
  171. appdir: std::env::var_os("APPDIR"),
  172. args,
  173. };
  174. if env.appimage.is_some() || env.appdir.is_some() {
  175. // validate that we're actually running on an AppImage
  176. // an AppImage is mounted to `/$TEMPDIR/.mount_${appPrefix}${hash}`
  177. // see https://github.com/AppImage/AppImageKit/blob/1681fd84dbe09c7d9b22e13cdb16ea601aa0ec47/src/runtime.c#L501
  178. // note that it is safe to use `std::env::current_exe` here since we just loaded an AppImage.
  179. let is_temp = std::env::current_exe()
  180. .map(|p| {
  181. p.display()
  182. .to_string()
  183. .starts_with(&format!("{}/.mount_", std::env::temp_dir().display()))
  184. })
  185. .unwrap_or(true);
  186. if !is_temp {
  187. warn!("`APPDIR` or `APPIMAGE` environment variable found but this application was not detected as an AppImage; this might be a security issue.");
  188. }
  189. }
  190. env
  191. }
  192. #[cfg(not(target_os = "linux"))]
  193. {
  194. Self { args }
  195. }
  196. }
  197. }
  198. /// The result type of `tauri-utils`.
  199. pub type Result<T> = std::result::Result<T, Error>;
  200. /// The error type of `tauri-utils`.
  201. #[derive(Debug, thiserror::Error)]
  202. #[non_exhaustive]
  203. pub enum Error {
  204. /// Target triple architecture error
  205. #[error("Unable to determine target-architecture")]
  206. Architecture,
  207. /// Target triple OS error
  208. #[error("Unable to determine target-os")]
  209. Os,
  210. /// Target triple environment error
  211. #[error("Unable to determine target-environment")]
  212. Environment,
  213. /// Tried to get resource on an unsupported platform
  214. #[error("Unsupported platform for reading resources")]
  215. UnsupportedPlatform,
  216. /// Get parent process error
  217. #[error("Could not get parent process")]
  218. ParentProcess,
  219. /// Get parent process PID error
  220. #[error("Could not get parent PID")]
  221. ParentPid,
  222. /// Get child process error
  223. #[error("Could not get child process")]
  224. ChildProcess,
  225. /// IO error
  226. #[error("{0}")]
  227. Io(#[from] std::io::Error),
  228. /// Invalid pattern.
  229. #[error("invalid pattern `{0}`. Expected either `brownfield` or `isolation`.")]
  230. InvalidPattern(String),
  231. /// Invalid glob pattern.
  232. #[cfg(feature = "resources")]
  233. #[error("{0}")]
  234. GlobPattern(#[from] glob::PatternError),
  235. /// Failed to use glob pattern.
  236. #[cfg(feature = "resources")]
  237. #[error("`{0}`")]
  238. Glob(#[from] glob::GlobError),
  239. /// Glob pattern did not find any results.
  240. #[cfg(feature = "resources")]
  241. #[error("path matching {0} not found.")]
  242. GlobPathNotFound(String),
  243. /// Error walking directory.
  244. #[cfg(feature = "resources")]
  245. #[error("{0}")]
  246. WalkdirError(#[from] walkdir::Error),
  247. /// Not allowed to walk dir.
  248. #[cfg(feature = "resources")]
  249. #[error("could not walk directory `{0}`, try changing `allow_walk` to true on the `ResourcePaths` constructor.")]
  250. NotAllowedToWalkDir(std::path::PathBuf),
  251. }
  252. /// Suppresses the unused-variable warnings of the given inputs.
  253. ///
  254. /// This does not move any values. Instead, it just suppresses the warning by taking a
  255. /// reference to the value.
  256. #[macro_export]
  257. macro_rules! consume_unused_variable {
  258. ($($arg:expr),*) => {
  259. $(
  260. let _ = &$arg;
  261. )*
  262. ()
  263. };
  264. }
  265. /// Prints to the standard error, with a newline.
  266. ///
  267. /// Equivalent to the [`eprintln!`] macro, except that it's only effective for debug builds.
  268. #[macro_export]
  269. macro_rules! debug_eprintln {
  270. () => ($crate::debug_eprintln!(""));
  271. ($($arg:tt)*) => {
  272. #[cfg(debug_assertions)]
  273. eprintln!($($arg)*);
  274. #[cfg(not(debug_assertions))]
  275. $crate::consume_unused_variable!($($arg)*);
  276. };
  277. }
  278. /// Reconstructs a path from its components using the platform separator then converts it to String and removes UNC prefixes on Windows if it exists.
  279. pub fn display_path<P: AsRef<Path>>(p: P) -> String {
  280. dunce::simplified(&p.as_ref().components().collect::<PathBuf>())
  281. .display()
  282. .to_string()
  283. }