path.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! Types and functions related to file system path operations.
  5. use std::{
  6. env::temp_dir,
  7. path::{Component, Path, PathBuf},
  8. };
  9. use crate::{Config, Env, PackageInfo};
  10. use serde_repr::{Deserialize_repr, Serialize_repr};
  11. /// A base directory to be used in [`resolve_path`].
  12. ///
  13. /// The base directory is the optional root of a file system operation.
  14. /// If informed by the API call, all paths will be relative to the path of the given directory.
  15. ///
  16. /// For more information, check the [`dirs_next` documentation](https://docs.rs/dirs_next/).
  17. #[derive(Serialize_repr, Deserialize_repr, Clone, Copy, Debug)]
  18. #[repr(u16)]
  19. #[non_exhaustive]
  20. pub enum BaseDirectory {
  21. /// The Audio directory.
  22. Audio = 1,
  23. /// The Cache directory.
  24. Cache,
  25. /// The Config directory.
  26. Config,
  27. /// The Data directory.
  28. Data,
  29. /// The LocalData directory.
  30. LocalData,
  31. /// The Desktop directory.
  32. Desktop,
  33. /// The Document directory.
  34. Document,
  35. /// The Download directory.
  36. Download,
  37. /// The Executable directory.
  38. Executable,
  39. /// The Font directory.
  40. Font,
  41. /// The Home directory.
  42. Home,
  43. /// The Picture directory.
  44. Picture,
  45. /// The Public directory.
  46. Public,
  47. /// The Runtime directory.
  48. Runtime,
  49. /// The Template directory.
  50. Template,
  51. /// The Video directory.
  52. Video,
  53. /// The Resource directory.
  54. Resource,
  55. /// The default App config directory.
  56. /// Resolves to [`BaseDirectory::Config`].
  57. App,
  58. /// The Log directory.
  59. /// Resolves to `BaseDirectory::Home/Library/Logs/{bundle_identifier}` on macOS
  60. /// and `BaseDirectory::Config/{bundle_identifier}/logs` on linux and windows.
  61. Log,
  62. /// A temporary directory.
  63. /// Resolves to [`temp_dir`].
  64. Temp,
  65. }
  66. impl BaseDirectory {
  67. /// Gets the variable that represents this [`BaseDirectory`] for string paths.
  68. pub fn variable(self) -> &'static str {
  69. match self {
  70. Self::Audio => "$AUDIO",
  71. Self::Cache => "$CACHE",
  72. Self::Config => "$CONFIG",
  73. Self::Data => "$DATA",
  74. Self::LocalData => "$LOCALDATA",
  75. Self::Desktop => "$DESKTOP",
  76. Self::Document => "$DOCUMENT",
  77. Self::Download => "$DOWNLOAD",
  78. Self::Executable => "$EXE",
  79. Self::Font => "$FONT",
  80. Self::Home => "$HOME",
  81. Self::Picture => "$PICTURE",
  82. Self::Public => "$PUBLIC",
  83. Self::Runtime => "$RUNTIME",
  84. Self::Template => "$TEMPLATE",
  85. Self::Video => "$VIDEO",
  86. Self::Resource => "$RESOURCE",
  87. Self::App => "$APP",
  88. Self::Log => "$LOG",
  89. Self::Temp => "$TEMP",
  90. }
  91. }
  92. /// Gets the [`BaseDirectory`] associated with the given variable, or [`None`] if the variable doesn't match any.
  93. pub fn from_variable(variable: &str) -> Option<Self> {
  94. let res = match variable {
  95. "$AUDIO" => Self::Audio,
  96. "$CACHE" => Self::Cache,
  97. "$CONFIG" => Self::Config,
  98. "$DATA" => Self::Data,
  99. "$LOCALDATA" => Self::LocalData,
  100. "$DESKTOP" => Self::Desktop,
  101. "$DOCUMENT" => Self::Document,
  102. "$DOWNLOAD" => Self::Download,
  103. "$EXE" => Self::Executable,
  104. "$FONT" => Self::Font,
  105. "$HOME" => Self::Home,
  106. "$PICTURE" => Self::Picture,
  107. "$PUBLIC" => Self::Public,
  108. "$RUNTIME" => Self::Runtime,
  109. "$TEMPLATE" => Self::Template,
  110. "$VIDEO" => Self::Video,
  111. "$RESOURCE" => Self::Resource,
  112. "$APP" => Self::App,
  113. "$LOG" => Self::Log,
  114. "$TEMP" => Self::Temp,
  115. _ => return None,
  116. };
  117. Some(res)
  118. }
  119. }
  120. /// Parse the given path, resolving a [`BaseDirectory`] variable if the path starts with one.
  121. ///
  122. /// # Examples
  123. ///
  124. /// ```rust,no_run
  125. /// use tauri::Manager;
  126. /// tauri::Builder::default()
  127. /// .setup(|app| {
  128. /// let path = tauri::api::path::parse(&app.config(), app.package_info(), &app.env(), "$HOME/.bashrc")?;
  129. /// assert_eq!(path.to_str().unwrap(), "/home/${whoami}/.bashrc");
  130. /// Ok(())
  131. /// });
  132. /// ```
  133. pub fn parse<P: AsRef<Path>>(
  134. config: &Config,
  135. package_info: &PackageInfo,
  136. env: &Env,
  137. path: P,
  138. ) -> crate::api::Result<PathBuf> {
  139. let mut p = PathBuf::new();
  140. let mut components = path.as_ref().components();
  141. match components.next() {
  142. Some(Component::Normal(str)) => {
  143. if let Some(base_directory) = BaseDirectory::from_variable(&str.to_string_lossy()) {
  144. p.push(resolve_path(
  145. config,
  146. package_info,
  147. env,
  148. "",
  149. Some(base_directory),
  150. )?);
  151. } else {
  152. p.push(str);
  153. }
  154. }
  155. Some(component) => p.push(component),
  156. None => (),
  157. }
  158. for component in components {
  159. if let Component::ParentDir = component {
  160. continue;
  161. }
  162. p.push(component);
  163. }
  164. println!("res {:?}", p);
  165. Ok(p)
  166. }
  167. /// Resolves the path with the optional base directory.
  168. ///
  169. /// This is a low level API. If the application has been built,
  170. /// prefer the [path resolver API](`crate::AppHandle#method.path_resolver`).
  171. ///
  172. /// # Examples
  173. ///
  174. /// ## Before initializing the application
  175. ///
  176. /// ```rust,no_run
  177. /// use tauri::{api::path::{BaseDirectory, resolve_path}, Env};
  178. /// // on an actual app, remove the string argument
  179. /// let context = tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json");
  180. /// let path = resolve_path(
  181. /// context.config(),
  182. /// context.package_info(),
  183. /// &Env::default(),
  184. /// "db/tauri.sqlite",
  185. /// Some(BaseDirectory::App))
  186. /// .expect("failed to resolve path");
  187. /// assert_eq!(path.to_str().unwrap(), "/home/${whoami}/.config/com.tauri.app/db/tauri.sqlite");
  188. ///
  189. /// tauri::Builder::default().run(context).expect("error while running tauri application");
  190. /// ```
  191. ///
  192. /// ## With an initialized app
  193. /// ```rust,no_run
  194. /// use tauri::{api::path::{BaseDirectory, resolve_path}, Manager};
  195. /// tauri::Builder::default()
  196. /// .setup(|app| {
  197. /// let path = resolve_path(
  198. /// &app.config(),
  199. /// app.package_info(),
  200. /// &app.env(),
  201. /// "path/to/something",
  202. /// Some(BaseDirectory::Config)
  203. /// )?;
  204. /// assert_eq!(path.to_str().unwrap(), "/home/${whoami}/.config/path/to/something");
  205. /// Ok(())
  206. /// });
  207. /// ```
  208. pub fn resolve_path<P: AsRef<Path>>(
  209. config: &Config,
  210. package_info: &PackageInfo,
  211. env: &Env,
  212. path: P,
  213. dir: Option<BaseDirectory>,
  214. ) -> crate::api::Result<PathBuf> {
  215. if let Some(base_dir) = dir {
  216. let resolve_resource = matches!(base_dir, BaseDirectory::Resource);
  217. let base_dir_path = match base_dir {
  218. BaseDirectory::Audio => audio_dir(),
  219. BaseDirectory::Cache => cache_dir(),
  220. BaseDirectory::Config => config_dir(),
  221. BaseDirectory::Data => data_dir(),
  222. BaseDirectory::LocalData => local_data_dir(),
  223. BaseDirectory::Desktop => desktop_dir(),
  224. BaseDirectory::Document => document_dir(),
  225. BaseDirectory::Download => download_dir(),
  226. BaseDirectory::Executable => executable_dir(),
  227. BaseDirectory::Font => font_dir(),
  228. BaseDirectory::Home => home_dir(),
  229. BaseDirectory::Picture => picture_dir(),
  230. BaseDirectory::Public => public_dir(),
  231. BaseDirectory::Runtime => runtime_dir(),
  232. BaseDirectory::Template => template_dir(),
  233. BaseDirectory::Video => video_dir(),
  234. BaseDirectory::Resource => resource_dir(package_info, env),
  235. BaseDirectory::App => app_dir(config),
  236. BaseDirectory::Log => log_dir(config),
  237. BaseDirectory::Temp => Some(temp_dir()),
  238. };
  239. if let Some(mut base_dir_path_value) = base_dir_path {
  240. // use the same path resolution mechanism as the bundler's resource injection algorithm
  241. if resolve_resource {
  242. let mut resource_path = PathBuf::new();
  243. for component in path.as_ref().components() {
  244. match component {
  245. Component::Prefix(_) => {}
  246. Component::RootDir => resource_path.push("_root_"),
  247. Component::CurDir => {}
  248. Component::ParentDir => resource_path.push("_up_"),
  249. Component::Normal(p) => resource_path.push(p),
  250. }
  251. }
  252. base_dir_path_value.push(resource_path);
  253. } else {
  254. base_dir_path_value.push(path);
  255. }
  256. Ok(base_dir_path_value)
  257. } else {
  258. Err(crate::api::Error::Path(
  259. "unable to determine base dir path".to_string(),
  260. ))
  261. }
  262. } else {
  263. let mut dir_path = PathBuf::new();
  264. dir_path.push(path);
  265. Ok(dir_path)
  266. }
  267. }
  268. /// Returns the path to the user's audio directory.
  269. pub fn audio_dir() -> Option<PathBuf> {
  270. dirs_next::audio_dir()
  271. }
  272. /// Returns the path to the user's cache directory.
  273. pub fn cache_dir() -> Option<PathBuf> {
  274. dirs_next::cache_dir()
  275. }
  276. /// Returns the path to the user's config directory.
  277. pub fn config_dir() -> Option<PathBuf> {
  278. dirs_next::config_dir()
  279. }
  280. /// Returns the path to the user's data directory.
  281. pub fn data_dir() -> Option<PathBuf> {
  282. dirs_next::data_dir()
  283. }
  284. /// Returns the path to the user's local data directory.
  285. pub fn local_data_dir() -> Option<PathBuf> {
  286. dirs_next::data_local_dir()
  287. }
  288. /// Returns the path to the user's desktop directory.
  289. pub fn desktop_dir() -> Option<PathBuf> {
  290. dirs_next::desktop_dir()
  291. }
  292. /// Returns the path to the user's document directory.
  293. pub fn document_dir() -> Option<PathBuf> {
  294. dirs_next::document_dir()
  295. }
  296. /// Returns the path to the user's download directory.
  297. pub fn download_dir() -> Option<PathBuf> {
  298. dirs_next::download_dir()
  299. }
  300. /// Returns the path to the user's executable directory.
  301. pub fn executable_dir() -> Option<PathBuf> {
  302. dirs_next::executable_dir()
  303. }
  304. /// Returns the path to the user's font directory.
  305. pub fn font_dir() -> Option<PathBuf> {
  306. dirs_next::font_dir()
  307. }
  308. /// Returns the path to the user's home directory.
  309. pub fn home_dir() -> Option<PathBuf> {
  310. dirs_next::home_dir()
  311. }
  312. /// Returns the path to the user's picture directory.
  313. pub fn picture_dir() -> Option<PathBuf> {
  314. dirs_next::picture_dir()
  315. }
  316. /// Returns the path to the user's public directory.
  317. pub fn public_dir() -> Option<PathBuf> {
  318. dirs_next::public_dir()
  319. }
  320. /// Returns the path to the user's runtime directory.
  321. pub fn runtime_dir() -> Option<PathBuf> {
  322. dirs_next::runtime_dir()
  323. }
  324. /// Returns the path to the user's template directory.
  325. pub fn template_dir() -> Option<PathBuf> {
  326. dirs_next::template_dir()
  327. }
  328. /// Returns the path to the user's video dir
  329. pub fn video_dir() -> Option<PathBuf> {
  330. dirs_next::video_dir()
  331. }
  332. /// Returns the path to the resource directory of this app.
  333. pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> Option<PathBuf> {
  334. crate::utils::platform::resource_dir(package_info, env).ok()
  335. }
  336. /// Returns the path to the suggested directory for your app config files.
  337. pub fn app_dir(config: &Config) -> Option<PathBuf> {
  338. dirs_next::config_dir().map(|dir| dir.join(&config.tauri.bundle.identifier))
  339. }
  340. /// Returns the path to the suggested log directory.
  341. pub fn log_dir(config: &Config) -> Option<PathBuf> {
  342. #[cfg(target_os = "macos")]
  343. let path = dirs_next::home_dir().map(|dir| {
  344. dir
  345. .join("Library/Logs")
  346. .join(&config.tauri.bundle.identifier)
  347. });
  348. #[cfg(not(target_os = "macos"))]
  349. let path =
  350. dirs_next::config_dir().map(|dir| dir.join(&config.tauri.bundle.identifier).join("logs"));
  351. path
  352. }