Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): add app-specific directory APIs, closes #5263 #5272

Merged
merged 13 commits into from Sep 28, 2022
80 changes: 65 additions & 15 deletions core/tauri/src/api/path.rs
Expand Up @@ -57,16 +57,26 @@ pub enum BaseDirectory {
Video,
/// The Resource directory.
Resource,
/// The default App config directory.
/// Resolves to [`BaseDirectory::Config`].
/// Alias of [`BaseDirectory::AppConfig`]
App,
/// The Log directory.
/// Resolves to `BaseDirectory::Home/Library/Logs/{bundle_identifier}` on macOS
/// and `BaseDirectory::Config/{bundle_identifier}/logs` on linux and windows.
/// Alias of [`BaseDirectory::AppLog`]
caesar marked this conversation as resolved.
Show resolved Hide resolved
Log,
/// A temporary directory.
/// Resolves to [`temp_dir`].
Temp,
/// The default App config directory.
/// Resolves to [`BaseDirectory::Config`]`/{bundle_identifier}`.
AppConfig,
/// The default App data directory.
/// Resolves to [`BaseDirectory::Data`]`/{bundle_identifier}`.
AppData,
/// The default App cache directory.
/// Resolves to [`BaseDirectory::Cache`]`/{bundle_identifier}`.
AppCache,
/// The default App log directory.
/// Resolves to [`BaseDirectory::Home`]`/Library/Logs/{bundle_identifier}` on macOS
/// and [`BaseDirectory::Config`]`/{bundle_identifier}/logs` on linux and Windows.
AppLog,
}

impl BaseDirectory {
Expand All @@ -93,6 +103,10 @@ impl BaseDirectory {
Self::App => "$APP",
Self::Log => "$LOG",
Self::Temp => "$TEMP",
Self::AppConfig => "$APPCONFIG",
Self::AppData => "$APPDATA",
Self::AppCache => "$APPCACHE",
Self::AppLog => "$APPLOG",
}
}

Expand All @@ -119,6 +133,10 @@ impl BaseDirectory {
"$APP" => Self::App,
"$LOG" => Self::Log,
"$TEMP" => Self::Temp,
"$APPCONFIG" => Self::AppConfig,
"$APPDATA" => Self::AppData,
"$APPCACHE" => Self::AppCache,
"$APPLOG" => Self::AppLog,
_ => return None,
};
Some(res)
Expand Down Expand Up @@ -245,6 +263,10 @@ pub fn resolve_path<P: AsRef<Path>>(
BaseDirectory::App => app_dir(config),
BaseDirectory::Log => log_dir(config),
BaseDirectory::Temp => Some(temp_dir()),
BaseDirectory::AppConfig => app_config_dir(config),
BaseDirectory::AppData => app_data_dir(config),
BaseDirectory::AppCache => app_cache_dir(config),
BaseDirectory::AppLog => app_log_dir(config),
};
if let Some(mut base_dir_path_value) = base_dir_path {
// use the same path resolution mechanism as the bundler's resource injection algorithm
Expand Down Expand Up @@ -459,25 +481,43 @@ pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> Option<PathBuf> {
crate::utils::platform::resource_dir(package_info, env).ok()
}

/// Returns the path to the suggested directory for your app config files.
/// Returns the path to the suggested directory for your app's config files.
///
/// Resolves to `${config_dir}/${bundle_identifier}`.
/// Resolves to [`config_dir`]`/${bundle_identifier}`.
///
/// See [`PathResolver::app_dir`](crate::PathResolver#method.app_dir) for a more convenient helper function.
pub fn app_dir(config: &Config) -> Option<PathBuf> {
/// See [`PathResolver::app_config_dir`](crate::PathResolver#method.app_config_dir) for a more convenient helper function.
pub fn app_config_dir(config: &Config) -> Option<PathBuf> {
dirs_next::config_dir().map(|dir| dir.join(&config.tauri.bundle.identifier))
}

/// Returns the path to the suggested log directory.
/// Returns the path to the suggested directory for your app's data files.
///
/// Resolves to [`data_dir`]`/${bundle_identifier}`.
///
/// See [`PathResolver::app_data_dir`](crate::PathResolver#method.app_data_dir) for a more convenient helper function.
pub fn app_data_dir(config: &Config) -> Option<PathBuf> {
dirs_next::data_dir().map(|dir| dir.join(&config.tauri.bundle.identifier))
}

/// Returns the path to the suggested directory for your app's cache files.
///
/// Resolves to [`cache_dir`]`/${bundle_identifier}`.
///
/// See [`PathResolver::app_cache_dir`](crate::PathResolver#method.app_cache_dir) for a more convenient helper function.
pub fn app_cache_dir(config: &Config) -> Option<PathBuf> {
dirs_next::cache_dir().map(|dir| dir.join(&config.tauri.bundle.identifier))
}

/// Returns the path to the suggested directory for your app's log files.
///
/// ## Platform-specific
///
/// - **Linux:** Resolves to `${config_dir}/${bundle_identifier}`.
/// - **macOS:** Resolves to `${home_dir}//Library/Logs/{bundle_identifier}`
/// - **Windows:** Resolves to `${config_dir}/${bundle_identifier}`.
/// - **Linux:** Resolves to [`config_dir`]`/${bundle_identifier}`.
/// - **macOS:** Resolves to [`home_dir`]`/Library/Logs/${bundle_identifier}`
/// - **Windows:** Resolves to [`config_dir`]`/${bundle_identifier}`.
///
/// See [`PathResolver::log_dir`](crate::PathResolver#method.log_dir) for a more convenient helper function.
pub fn log_dir(config: &Config) -> Option<PathBuf> {
/// See [`PathResolver::app_log_dir`](crate::PathResolver#method.app_log_dir) for a more convenient helper function.
pub fn app_log_dir(config: &Config) -> Option<PathBuf> {
#[cfg(target_os = "macos")]
let path = dirs_next::home_dir().map(|dir| {
dir
Expand All @@ -491,3 +531,13 @@ pub fn log_dir(config: &Config) -> Option<PathBuf> {

path
}

/// Alias of [`app_config_dir`] for backwards-compatibility purposes.
pub fn app_dir(config: &Config) -> Option<PathBuf> {
app_config_dir(config)
}

/// Alias of [`app_log_dir`] for backwards-compatibility purposes.
pub fn log_dir(config: &Config) -> Option<PathBuf> {
app_log_dir(config)
}
caesar marked this conversation as resolved.
Show resolved Hide resolved
28 changes: 24 additions & 4 deletions core/tauri/src/app.rs
Expand Up @@ -288,14 +288,34 @@ impl PathResolver {
.map(|dir| dir.join(resource_relpath(path.as_ref())))
}

/// Returns the path to the suggested directory for your app config files.
/// Returns the path to the suggested directory for your app's config files.
pub fn app_config_dir(&self) -> Option<PathBuf> {
crate::api::path::app_config_dir(&self.config)
}

/// Returns the path to the suggested directory for your app's data files.
pub fn app_data_dir(&self) -> Option<PathBuf> {
crate::api::path::app_data_dir(&self.config)
}

/// Returns the path to the suggested directory for your app's cache files.
pub fn app_cache_dir(&self) -> Option<PathBuf> {
crate::api::path::app_cache_dir(&self.config)
}

/// Returns the path to the suggested directory for your app's log files.
pub fn app_log_dir(&self) -> Option<PathBuf> {
crate::api::path::app_log_dir(&self.config)
}

/// Alias of [`PathResolver::app_config_dir`] for backwards-compatibility purposes.
pub fn app_dir(&self) -> Option<PathBuf> {
crate::api::path::app_dir(&self.config)
self.app_config_dir()
}

/// Returns the path to the suggested log directory.
/// Alias of [`PathResolver::app_log_dir`] for backwards-compatibility purposes.
pub fn log_dir(&self) -> Option<PathBuf> {
crate::api::path::log_dir(&self.config)
self.app_log_dir()
}
caesar marked this conversation as resolved.
Show resolved Hide resolved
}

Expand Down
61 changes: 33 additions & 28 deletions tooling/api/src/fs.ts
Expand Up @@ -41,21 +41,22 @@
*
* The scope configuration is an array of glob patterns describing folder paths that are allowed.
* For instance, this scope configuration only allows accessing files on the
* *databases* folder of the {@link path.appDir | $APP directory}:
* *databases* folder of the {@link path.appDataDir | $APPDATA directory}:
* ```json
* {
* "tauri": {
* "allowlist": {
* "fs": {
* "scope": ["$APP/databases/*"]
* "scope": ["$APPDATA/databases/*"]
* }
* }
* }
* }
* ```
*
* Notice the use of the `$APP` variable. The value is injected at runtime, resolving to the {@link path.appDir | app directory}.
* Notice the use of the `$APPDATA` variable. The value is injected at runtime, resolving to the {@link path.appDataDir | app data directory}.
caesar marked this conversation as resolved.
Show resolved Hide resolved
* The available variables are:
* {@link path.appConfigDir | `$APPCONFIG`}, {@link path.appDataDir | `$APPDATA`}, {@link path.appCacheDir | `$APPCACHE`}, {@link path.appLogDir | `$APPLOG`},
* {@link path.audioDir | `$AUDIO`}, {@link path.cacheDir | `$CACHE`}, {@link path.configDir | `$CONFIG`}, {@link path.dataDir | `$DATA`},
* {@link path.localDataDir | `$LOCALDATA`}, {@link path.desktopDir | `$DESKTOP`}, {@link path.documentDir | `$DOCUMENT`},
* {@link path.downloadDir | `$DOWNLOAD`}, {@link path.executableDir | `$EXE`}, {@link path.fontDir | `$FONT`}, {@link path.homeDir | `$HOME`},
Expand Down Expand Up @@ -95,7 +96,11 @@ export enum BaseDirectory {
Resource,
App,
Log,
Temp
Temp,
AppConfig,
AppData,
AppCache,
AppLog,
}

/**
Expand Down Expand Up @@ -159,8 +164,8 @@ interface FileEntry {
* @example
* ```typescript
* import { readTextFile, BaseDirectory } from '@tauri-apps/api/fs';
* // Read the text file in the `$APPDIR/app.conf` path
* const contents = await readTextFile('app.conf', { dir: BaseDirectory.App });
* // Read the text file in the `$APPCONFIG/app.conf` path
* const contents = await readTextFile('app.conf', { dir: BaseDirectory.AppConfig });
* ```
*
* @since 1.0.0
Expand Down Expand Up @@ -211,8 +216,8 @@ async function readBinaryFile(
* @example
* ```typescript
* import { writeTextFile, BaseDirectory } from '@tauri-apps/api/fs';
* // Write a text file to the `$APPDIR/app.conf` path
* await writeTextFile('app.conf', 'file contents', { dir: BaseDirectory.App });
* // Write a text file to the `$APPCONFIG/app.conf` path
* await writeTextFile('app.conf', 'file contents', { dir: BaseDirectory.AppConfig });
* ```
*
* @since 1.0.0
Expand All @@ -228,8 +233,8 @@ async function writeTextFile(
* @example
* ```typescript
* import { writeTextFile, BaseDirectory } from '@tauri-apps/api/fs';
* // Write a text file to the `$APPDIR/app.conf` path
* await writeTextFile({ path: 'app.conf', contents: 'file contents' }, { dir: BaseDirectory.App });
* // Write a text file to the `$APPCONFIG/app.conf` path
* await writeTextFile({ path: 'app.conf', contents: 'file contents' }, { dir: BaseDirectory.AppConfig });
* ```
* @returns A promise indicating the success or failure of the operation.
*
Expand Down Expand Up @@ -290,8 +295,8 @@ async function writeTextFile(
* @example
* ```typescript
* import { writeBinaryFile, BaseDirectory } from '@tauri-apps/api/fs';
* // Write a binary file to the `$APPDIR/avatar.png` path
* await writeBinaryFile('avatar.png', new Uint8Array([]), { dir: BaseDirectory.App });
* // Write a binary file to the `$APPDATA/avatar.png` path
* await writeBinaryFile('avatar.png', new Uint8Array([]), { dir: BaseDirectory.AppData });
* ```
*
* @param options Configuration object.
Expand All @@ -310,8 +315,8 @@ async function writeBinaryFile(
* @example
* ```typescript
* import { writeBinaryFile, BaseDirectory } from '@tauri-apps/api/fs';
* // Write a binary file to the `$APPDIR/avatar.png` path
* await writeBinaryFile({ path: 'avatar.png', contents: new Uint8Array([]) }, { dir: BaseDirectory.App });
* // Write a binary file to the `$APPDATA/avatar.png` path
* await writeBinaryFile({ path: 'avatar.png', contents: new Uint8Array([]) }, { dir: BaseDirectory.AppData });
* ```
*
* @param file The object containing the file path and contents.
Expand Down Expand Up @@ -380,8 +385,8 @@ async function writeBinaryFile(
* @example
* ```typescript
* import { readDir, BaseDirectory } from '@tauri-apps/api/fs';
* // Reads the `$APPDIR/users` directory recursively
* const entries = await readDir('users', { dir: BaseDirectory.App, recursive: true });
* // Reads the `$APPDATA/users` directory recursively
* const entries = await readDir('users', { dir: BaseDirectory.AppData, recursive: true });
*
* function processEntries(entries) {
* for (const entry of entries) {
Expand Down Expand Up @@ -416,8 +421,8 @@ async function readDir(
* @example
* ```typescript
* import { createDir, BaseDirectory } from '@tauri-apps/api/fs';
* // Create the `$APPDIR/users` directory
* await createDir('users', { dir: BaseDirectory.App, recursive: true });
* // Create the `$APPDATA/users` directory
* await createDir('users', { dir: BaseDirectory.AppData, recursive: true });
* ```
*
* @returns A promise indicating the success or failure of the operation.
Expand All @@ -444,8 +449,8 @@ async function createDir(
* @example
* ```typescript
* import { removeDir, BaseDirectory } from '@tauri-apps/api/fs';
* // Remove the directory `$APPDIR/users`
* await removeDir('users', { dir: BaseDirectory.App });
* // Remove the directory `$APPDATA/users`
* await removeDir('users', { dir: BaseDirectory.AppData });
* ```
*
* @returns A promise indicating the success or failure of the operation.
Expand All @@ -471,8 +476,8 @@ async function removeDir(
* @example
* ```typescript
* import { copyFile, BaseDirectory } from '@tauri-apps/api/fs';
* // Copy the `$APPDIR/app.conf` file to `$APPDIR/app.conf.bk`
* await copyFile('app.conf', 'app.conf.bk', { dir: BaseDirectory.App });
* // Copy the `$APPCONFIG/app.conf` file to `$APPCONFIG/app.conf.bk`
* await copyFile('app.conf', 'app.conf.bk', { dir: BaseDirectory.AppConfig });
* ```
*
* @returns A promise indicating the success or failure of the operation.
Expand Down Expand Up @@ -500,8 +505,8 @@ async function copyFile(
* @example
* ```typescript
* import { removeFile, BaseDirectory } from '@tauri-apps/api/fs';
* // Remove the `$APPDIR/app.conf` file
* await removeFile('app.conf', { dir: BaseDirectory.App });
* // Remove the `$APPConfig/app.conf` file
* await removeFile('app.conf', { dir: BaseDirectory.AppConfig });
* ```
*
* @returns A promise indicating the success or failure of the operation.
Expand All @@ -527,8 +532,8 @@ async function removeFile(
* @example
* ```typescript
* import { renameFile, BaseDirectory } from '@tauri-apps/api/fs';
* // Rename the `$APPDIR/avatar.png` file
* await renameFile('avatar.png', 'deleted.png', { dir: BaseDirectory.App });
* // Rename the `$APPDATA/avatar.png` file
* await renameFile('avatar.png', 'deleted.png', { dir: BaseDirectory.AppData });
* ```
*
* @returns A promise indicating the success or failure of the operation.
Expand Down Expand Up @@ -556,8 +561,8 @@ async function renameFile(
* @example
* ```typescript
* import { exists, BaseDirectory } from '@tauri-apps/api/fs';
* // Check if the `$APPDIR/avatar.png` file exists
* await exists('avatar.png', { dir: BaseDirectory.App });
* // Check if the `$APPDATA/avatar.png` file exists
* await exists('avatar.png', { dir: BaseDirectory.AppData });
* ```
*
* @since 1.1.0
Expand Down