mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-12-01 03:02:28 +03:00
feat(core): add path plugin, implement mobile APIs (#6339)
This commit is contained in:
parent
1eacd51d18
commit
be941b9719
@ -32,7 +32,7 @@ tauri-winres = "0.1"
|
||||
semver = "1"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
swift-rs = { version = "1.0.3", features = [ "build" ] }
|
||||
swift-rs = { version = "1.0.4", features = [ "build" ] }
|
||||
|
||||
[features]
|
||||
codegen = [ "tauri-codegen", "quote" ]
|
||||
|
@ -4,17 +4,18 @@
|
||||
|
||||
use heck::{ToLowerCamelCase, ToSnakeCase};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
ext::IdentExt,
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input,
|
||||
spanned::Spanned,
|
||||
FnArg, Ident, ItemFn, Lit, Meta, Pat, Token, Visibility,
|
||||
FnArg, ItemFn, Lit, Meta, Pat, Token, Visibility,
|
||||
};
|
||||
|
||||
struct WrapperAttributes {
|
||||
root: TokenStream2,
|
||||
execution_context: ExecutionContext,
|
||||
argument_case: ArgumentCase,
|
||||
}
|
||||
@ -22,6 +23,7 @@ struct WrapperAttributes {
|
||||
impl Parse for WrapperAttributes {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mut wrapper_attributes = WrapperAttributes {
|
||||
root: quote!(::tauri),
|
||||
execution_context: ExecutionContext::Blocking,
|
||||
argument_case: ArgumentCase::Camel,
|
||||
};
|
||||
@ -43,6 +45,17 @@ impl Parse for WrapperAttributes {
|
||||
}
|
||||
};
|
||||
}
|
||||
} else if v.path.is_ident("root") {
|
||||
if let Lit::Str(s) = v.lit {
|
||||
let lit = s.value();
|
||||
|
||||
wrapper_attributes.root = if lit == "crate" {
|
||||
quote!($crate)
|
||||
} else {
|
||||
let ident = Ident::new(&lit, Span::call_site());
|
||||
quote!(#ident)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Meta::Path(p)) => {
|
||||
@ -104,21 +117,28 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
};
|
||||
|
||||
// body to the command wrapper or a `compile_error!` of an error occurred while parsing it.
|
||||
let body = syn::parse::<WrapperAttributes>(attributes)
|
||||
let (body, attributes) = syn::parse::<WrapperAttributes>(attributes)
|
||||
.map(|mut attrs| {
|
||||
if function.sig.asyncness.is_some() {
|
||||
attrs.execution_context = ExecutionContext::Async;
|
||||
}
|
||||
attrs
|
||||
})
|
||||
.and_then(|attrs| match attrs.execution_context {
|
||||
ExecutionContext::Async => body_async(&function, &invoke, attrs.argument_case),
|
||||
ExecutionContext::Blocking => body_blocking(&function, &invoke, attrs.argument_case),
|
||||
.and_then(|attrs| {
|
||||
let body = match attrs.execution_context {
|
||||
ExecutionContext::Async => body_async(&function, &invoke, &attrs),
|
||||
ExecutionContext::Blocking => body_blocking(&function, &invoke, &attrs),
|
||||
};
|
||||
body.map(|b| (b, Some(attrs)))
|
||||
})
|
||||
.unwrap_or_else(syn::Error::into_compile_error);
|
||||
.unwrap_or_else(|e| (syn::Error::into_compile_error(e), None));
|
||||
|
||||
let Invoke { message, resolver } = invoke;
|
||||
|
||||
let root = attributes
|
||||
.map(|a| a.root)
|
||||
.unwrap_or_else(|| quote!(::tauri));
|
||||
|
||||
// Rely on rust 2018 edition to allow importing a macro from a path.
|
||||
quote!(
|
||||
#function
|
||||
@ -129,10 +149,10 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
// double braces because the item is expected to be a block expression
|
||||
($path:path, $invoke:ident) => {{
|
||||
#[allow(unused_imports)]
|
||||
use ::tauri::command::private::*;
|
||||
use #root::command::private::*;
|
||||
// prevent warnings when the body is a `compile_error!` or if the command has no arguments
|
||||
#[allow(unused_variables)]
|
||||
let ::tauri::Invoke { message: #message, resolver: #resolver } = $invoke;
|
||||
let #root::Invoke { message: #message, resolver: #resolver } = $invoke;
|
||||
|
||||
#body
|
||||
}};
|
||||
@ -150,9 +170,13 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// See the [`tauri::command`] module for all the items and traits that make this possible.
|
||||
///
|
||||
/// [`tauri::command`]: https://docs.rs/tauri/*/tauri/runtime/index.html
|
||||
fn body_async(function: &ItemFn, invoke: &Invoke, case: ArgumentCase) -> syn::Result<TokenStream2> {
|
||||
fn body_async(
|
||||
function: &ItemFn,
|
||||
invoke: &Invoke,
|
||||
attributes: &WrapperAttributes,
|
||||
) -> syn::Result<TokenStream2> {
|
||||
let Invoke { message, resolver } = invoke;
|
||||
parse_args(function, message, case).map(|args| {
|
||||
parse_args(function, message, attributes).map(|args| {
|
||||
quote! {
|
||||
#resolver.respond_async_serialized(async move {
|
||||
let result = $path(#(#args?),*);
|
||||
@ -172,15 +196,15 @@ fn body_async(function: &ItemFn, invoke: &Invoke, case: ArgumentCase) -> syn::Re
|
||||
fn body_blocking(
|
||||
function: &ItemFn,
|
||||
invoke: &Invoke,
|
||||
case: ArgumentCase,
|
||||
attributes: &WrapperAttributes,
|
||||
) -> syn::Result<TokenStream2> {
|
||||
let Invoke { message, resolver } = invoke;
|
||||
let args = parse_args(function, message, case)?;
|
||||
let args = parse_args(function, message, attributes)?;
|
||||
|
||||
// the body of a `match` to early return any argument that wasn't successful in parsing.
|
||||
let match_body = quote!({
|
||||
Ok(arg) => arg,
|
||||
Err(err) => {#resolver.invoke_error(err); return true },
|
||||
Err(err) => { #resolver.invoke_error(err); return true },
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
@ -195,13 +219,13 @@ fn body_blocking(
|
||||
fn parse_args(
|
||||
function: &ItemFn,
|
||||
message: &Ident,
|
||||
case: ArgumentCase,
|
||||
attributes: &WrapperAttributes,
|
||||
) -> syn::Result<Vec<TokenStream2>> {
|
||||
function
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|arg| parse_arg(&function.sig.ident, arg, message, case))
|
||||
.map(|arg| parse_arg(&function.sig.ident, arg, message, attributes))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -210,7 +234,7 @@ fn parse_arg(
|
||||
command: &Ident,
|
||||
arg: &FnArg,
|
||||
message: &Ident,
|
||||
case: ArgumentCase,
|
||||
attributes: &WrapperAttributes,
|
||||
) -> syn::Result<TokenStream2> {
|
||||
// we have no use for self arguments
|
||||
let mut arg = match arg {
|
||||
@ -245,7 +269,7 @@ fn parse_arg(
|
||||
));
|
||||
}
|
||||
|
||||
match case {
|
||||
match attributes.argument_case {
|
||||
ArgumentCase::Camel => {
|
||||
key = key.to_lower_camel_case();
|
||||
}
|
||||
@ -254,8 +278,10 @@ fn parse_arg(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(quote!(::tauri::command::CommandArg::from_command(
|
||||
::tauri::command::CommandItem {
|
||||
let root = &attributes.root;
|
||||
|
||||
Ok(quote!(#root::command::CommandArg::from_command(
|
||||
#root::command::CommandItem {
|
||||
name: stringify!(#command),
|
||||
key: #key,
|
||||
message: &#message,
|
||||
|
@ -116,7 +116,7 @@ jni = "0.20"
|
||||
libc = "0.2"
|
||||
objc = "0.2"
|
||||
cocoa = "0.24"
|
||||
swift-rs = "1.0.3"
|
||||
swift-rs = "1.0.4"
|
||||
|
||||
[build-dependencies]
|
||||
heck = "0.4"
|
||||
|
@ -0,0 +1,78 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Environment
|
||||
import app.tauri.annotation.Command
|
||||
import app.tauri.annotation.TauriPlugin
|
||||
import app.tauri.plugin.Plugin
|
||||
import app.tauri.plugin.Invoke
|
||||
import app.tauri.plugin.JSObject
|
||||
|
||||
@TauriPlugin
|
||||
class PathPlugin(private val activity: Activity): Plugin(activity) {
|
||||
private fun resolvePath(invoke: Invoke, path: String?) {
|
||||
val obj = JSObject()
|
||||
obj.put("path", path)
|
||||
invoke.resolve(obj)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getAudioDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_MUSIC)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getExternalCacheDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.externalCacheDir?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getConfigDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.dataDir.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getDataDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.dataDir.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getDocumentDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getDownloadDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getPictureDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getPublicDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DCIM)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getVideoDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.externalCacheDir?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getResourcesDir(invoke: Invoke) {
|
||||
// TODO
|
||||
resolvePath(invoke, activity.cacheDir.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getCacheDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.cacheDir.absolutePath)
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -13,7 +13,6 @@ pub mod file;
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "http-api")))]
|
||||
pub mod http;
|
||||
pub mod ipc;
|
||||
pub mod path;
|
||||
pub mod process;
|
||||
#[cfg(feature = "shell-open-api")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "shell-open-api")))]
|
||||
|
@ -1,604 +0,0 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! Types and functions related to file system path operations.
|
||||
|
||||
use std::{
|
||||
env::temp_dir,
|
||||
path::{Component, Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::{Config, Env, PackageInfo};
|
||||
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
// we have to wrap the BaseDirectory enum in a module for #[allow(deprecated)]
|
||||
// to work, because the procedural macros on the enum prevent it from working directly
|
||||
// TODO: remove this workaround in v2 along with deprecated variants
|
||||
#[allow(deprecated)]
|
||||
mod base_directory {
|
||||
use super::*;
|
||||
|
||||
/// A base directory to be used in [`resolve_path`].
|
||||
///
|
||||
/// The base directory is the optional root of a file system operation.
|
||||
/// If informed by the API call, all paths will be relative to the path of the given directory.
|
||||
///
|
||||
/// For more information, check the [`dirs_next` documentation](https://docs.rs/dirs_next/).
|
||||
#[derive(Serialize_repr, Deserialize_repr, Clone, Copy, Debug)]
|
||||
#[repr(u16)]
|
||||
#[non_exhaustive]
|
||||
pub enum BaseDirectory {
|
||||
/// The Audio directory.
|
||||
Audio = 1,
|
||||
/// The Cache directory.
|
||||
Cache,
|
||||
/// The Config directory.
|
||||
Config,
|
||||
/// The Data directory.
|
||||
Data,
|
||||
/// The LocalData directory.
|
||||
LocalData,
|
||||
/// The Desktop directory.
|
||||
Desktop,
|
||||
/// The Document directory.
|
||||
Document,
|
||||
/// The Download directory.
|
||||
Download,
|
||||
/// The Executable directory.
|
||||
Executable,
|
||||
/// The Font directory.
|
||||
Font,
|
||||
/// The Home directory.
|
||||
Home,
|
||||
/// The Picture directory.
|
||||
Picture,
|
||||
/// The Public directory.
|
||||
Public,
|
||||
/// The Runtime directory.
|
||||
Runtime,
|
||||
/// The Template directory.
|
||||
Template,
|
||||
/// The Video directory.
|
||||
Video,
|
||||
/// The Resource directory.
|
||||
Resource,
|
||||
/// The default app config directory.
|
||||
/// Resolves to [`BaseDirectory::Config`]`/{bundle_identifier}`.
|
||||
#[deprecated(
|
||||
since = "1.2.0",
|
||||
note = "Will be removed in 2.0.0. Use `BaseDirectory::AppConfig` or BaseDirectory::AppData` instead."
|
||||
)]
|
||||
App,
|
||||
/// 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.
|
||||
#[deprecated(
|
||||
since = "1.2.0",
|
||||
note = "Will be removed in 2.0.0. Use `BaseDirectory::AppLog` instead."
|
||||
)]
|
||||
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 local data directory.
|
||||
/// Resolves to [`BaseDirectory::LocalData`]`/{bundle_identifier}`.
|
||||
AppLocalData,
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
pub use base_directory::BaseDirectory;
|
||||
|
||||
impl BaseDirectory {
|
||||
/// Gets the variable that represents this [`BaseDirectory`] for string paths.
|
||||
pub fn variable(self) -> &'static str {
|
||||
match self {
|
||||
Self::Audio => "$AUDIO",
|
||||
Self::Cache => "$CACHE",
|
||||
Self::Config => "$CONFIG",
|
||||
Self::Data => "$DATA",
|
||||
Self::LocalData => "$LOCALDATA",
|
||||
Self::Desktop => "$DESKTOP",
|
||||
Self::Document => "$DOCUMENT",
|
||||
Self::Download => "$DOWNLOAD",
|
||||
Self::Executable => "$EXE",
|
||||
Self::Font => "$FONT",
|
||||
Self::Home => "$HOME",
|
||||
Self::Picture => "$PICTURE",
|
||||
Self::Public => "$PUBLIC",
|
||||
Self::Runtime => "$RUNTIME",
|
||||
Self::Template => "$TEMPLATE",
|
||||
Self::Video => "$VIDEO",
|
||||
Self::Resource => "$RESOURCE",
|
||||
#[allow(deprecated)]
|
||||
Self::App => "$APP",
|
||||
#[allow(deprecated)]
|
||||
Self::Log => "$LOG",
|
||||
Self::Temp => "$TEMP",
|
||||
Self::AppConfig => "$APPCONFIG",
|
||||
Self::AppData => "$APPDATA",
|
||||
Self::AppLocalData => "$APPLOCALDATA",
|
||||
Self::AppCache => "$APPCACHE",
|
||||
Self::AppLog => "$APPLOG",
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the [`BaseDirectory`] associated with the given variable, or [`None`] if the variable doesn't match any.
|
||||
pub fn from_variable(variable: &str) -> Option<Self> {
|
||||
let res = match variable {
|
||||
"$AUDIO" => Self::Audio,
|
||||
"$CACHE" => Self::Cache,
|
||||
"$CONFIG" => Self::Config,
|
||||
"$DATA" => Self::Data,
|
||||
"$LOCALDATA" => Self::LocalData,
|
||||
"$DESKTOP" => Self::Desktop,
|
||||
"$DOCUMENT" => Self::Document,
|
||||
"$DOWNLOAD" => Self::Download,
|
||||
"$EXE" => Self::Executable,
|
||||
"$FONT" => Self::Font,
|
||||
"$HOME" => Self::Home,
|
||||
"$PICTURE" => Self::Picture,
|
||||
"$PUBLIC" => Self::Public,
|
||||
"$RUNTIME" => Self::Runtime,
|
||||
"$TEMPLATE" => Self::Template,
|
||||
"$VIDEO" => Self::Video,
|
||||
"$RESOURCE" => Self::Resource,
|
||||
#[allow(deprecated)]
|
||||
"$APP" => Self::App,
|
||||
#[allow(deprecated)]
|
||||
"$LOG" => Self::Log,
|
||||
"$TEMP" => Self::Temp,
|
||||
"$APPCONFIG" => Self::AppConfig,
|
||||
"$APPDATA" => Self::AppData,
|
||||
"$APPLOCALDATA" => Self::AppLocalData,
|
||||
"$APPCACHE" => Self::AppCache,
|
||||
"$APPLOG" => Self::AppLog,
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the given path, resolving a [`BaseDirectory`] variable if the path starts with one.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use tauri::Manager;
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let path = tauri::api::path::parse(&app.config(), app.package_info(), &app.env(), "$HOME/.bashrc")?;
|
||||
/// assert_eq!(path.to_str().unwrap(), "/home/${whoami}/.bashrc");
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn parse<P: AsRef<Path>>(
|
||||
config: &Config,
|
||||
package_info: &PackageInfo,
|
||||
env: &Env,
|
||||
path: P,
|
||||
) -> crate::api::Result<PathBuf> {
|
||||
let mut p = PathBuf::new();
|
||||
let mut components = path.as_ref().components();
|
||||
match components.next() {
|
||||
Some(Component::Normal(str)) => {
|
||||
if let Some(base_directory) = BaseDirectory::from_variable(&str.to_string_lossy()) {
|
||||
p.push(resolve_path(
|
||||
config,
|
||||
package_info,
|
||||
env,
|
||||
"",
|
||||
Some(base_directory),
|
||||
)?);
|
||||
} else {
|
||||
p.push(str);
|
||||
}
|
||||
}
|
||||
Some(component) => p.push(component),
|
||||
None => (),
|
||||
}
|
||||
|
||||
for component in components {
|
||||
if let Component::ParentDir = component {
|
||||
continue;
|
||||
}
|
||||
p.push(component);
|
||||
}
|
||||
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
/// Resolves the path with the optional base directory.
|
||||
///
|
||||
/// This is a low level API. If the application has been built,
|
||||
/// prefer the [path resolver API](`crate::AppHandle#method.path_resolver`).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Before initializing the application
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use tauri::{api::path::{BaseDirectory, resolve_path}, Env};
|
||||
/// // on an actual app, remove the string argument
|
||||
/// let context = tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json");
|
||||
/// let path = resolve_path(
|
||||
/// context.config(),
|
||||
/// context.package_info(),
|
||||
/// &Env::default(),
|
||||
/// "db/tauri.sqlite",
|
||||
/// Some(BaseDirectory::AppData))
|
||||
/// .expect("failed to resolve path");
|
||||
/// assert_eq!(path.to_str().unwrap(), "/home/${whoami}/.config/com.tauri.app/db/tauri.sqlite");
|
||||
///
|
||||
/// tauri::Builder::default().run(context).expect("error while running tauri application");
|
||||
/// ```
|
||||
///
|
||||
/// ## With an initialized app
|
||||
/// ```rust,no_run
|
||||
/// use tauri::{api::path::{BaseDirectory, resolve_path}, Manager};
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let path = resolve_path(
|
||||
/// &app.config(),
|
||||
/// app.package_info(),
|
||||
/// &app.env(),
|
||||
/// "path/to/something",
|
||||
/// Some(BaseDirectory::Config)
|
||||
/// )?;
|
||||
/// assert_eq!(path.to_str().unwrap(), "/home/${whoami}/.config/path/to/something");
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn resolve_path<P: AsRef<Path>>(
|
||||
config: &Config,
|
||||
package_info: &PackageInfo,
|
||||
env: &Env,
|
||||
path: P,
|
||||
dir: Option<BaseDirectory>,
|
||||
) -> crate::api::Result<PathBuf> {
|
||||
if let Some(base_dir) = dir {
|
||||
let resolve_resource = matches!(base_dir, BaseDirectory::Resource);
|
||||
let base_dir_path = match base_dir {
|
||||
BaseDirectory::Audio => audio_dir(),
|
||||
BaseDirectory::Cache => cache_dir(),
|
||||
BaseDirectory::Config => config_dir(),
|
||||
BaseDirectory::Data => data_dir(),
|
||||
BaseDirectory::LocalData => local_data_dir(),
|
||||
BaseDirectory::Desktop => desktop_dir(),
|
||||
BaseDirectory::Document => document_dir(),
|
||||
BaseDirectory::Download => download_dir(),
|
||||
BaseDirectory::Executable => executable_dir(),
|
||||
BaseDirectory::Font => font_dir(),
|
||||
BaseDirectory::Home => home_dir(),
|
||||
BaseDirectory::Picture => picture_dir(),
|
||||
BaseDirectory::Public => public_dir(),
|
||||
BaseDirectory::Runtime => runtime_dir(),
|
||||
BaseDirectory::Template => template_dir(),
|
||||
BaseDirectory::Video => video_dir(),
|
||||
BaseDirectory::Resource => resource_dir(package_info, env),
|
||||
#[allow(deprecated)]
|
||||
BaseDirectory::App => app_config_dir(config),
|
||||
#[allow(deprecated)]
|
||||
BaseDirectory::Log => app_log_dir(config),
|
||||
BaseDirectory::Temp => Some(temp_dir()),
|
||||
BaseDirectory::AppConfig => app_config_dir(config),
|
||||
BaseDirectory::AppData => app_data_dir(config),
|
||||
BaseDirectory::AppLocalData => app_local_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
|
||||
if resolve_resource {
|
||||
let mut resource_path = PathBuf::new();
|
||||
for component in path.as_ref().components() {
|
||||
match component {
|
||||
Component::Prefix(_) => {}
|
||||
Component::RootDir => resource_path.push("_root_"),
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => resource_path.push("_up_"),
|
||||
Component::Normal(p) => resource_path.push(p),
|
||||
}
|
||||
}
|
||||
base_dir_path_value.push(resource_path);
|
||||
} else {
|
||||
base_dir_path_value.push(path);
|
||||
}
|
||||
Ok(base_dir_path_value)
|
||||
} else {
|
||||
Err(crate::api::Error::Path(
|
||||
"unable to determine base dir path".to_string(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
let mut dir_path = PathBuf::new();
|
||||
dir_path.push(path);
|
||||
Ok(dir_path)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the user's audio directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_MUSIC_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Music`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Music}`.
|
||||
pub fn audio_dir() -> Option<PathBuf> {
|
||||
dirs_next::audio_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's cache directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_CACHE_HOME` or `$HOME/.cache`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Caches`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_LocalAppData}`.
|
||||
pub fn cache_dir() -> Option<PathBuf> {
|
||||
dirs_next::cache_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's config directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_CONFIG_HOME` or `$HOME/.config`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Application Support`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_RoamingAppData}`.
|
||||
pub fn config_dir() -> Option<PathBuf> {
|
||||
dirs_next::config_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's data directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_DATA_HOME` or `$HOME/.local/share`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Application Support`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_RoamingAppData}`.
|
||||
pub fn data_dir() -> Option<PathBuf> {
|
||||
dirs_next::data_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's local data directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_DATA_HOME` or `$HOME/.local/share`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Application Support`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_LocalAppData}`.
|
||||
pub fn local_data_dir() -> Option<PathBuf> {
|
||||
dirs_next::data_local_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's desktop directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_DESKTOP_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Desktop`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Desktop}`.
|
||||
pub fn desktop_dir() -> Option<PathBuf> {
|
||||
dirs_next::desktop_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's document directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_DOCUMENTS_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Documents`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Documents}`.
|
||||
pub fn document_dir() -> Option<PathBuf> {
|
||||
dirs_next::document_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's download directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_DOWNLOAD_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Downloads`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Downloads}`.
|
||||
pub fn download_dir() -> Option<PathBuf> {
|
||||
dirs_next::download_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's executable directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_BIN_HOME/../bin` or `$XDG_DATA_HOME/../bin` or `$HOME/.local/bin`.
|
||||
/// - **macOS:** Not supported.
|
||||
/// - **Windows:** Not supported.
|
||||
pub fn executable_dir() -> Option<PathBuf> {
|
||||
dirs_next::executable_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's font directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_DATA_HOME/fonts` or `$HOME/.local/share/fonts`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Fonts`.
|
||||
/// - **Windows:** Not supported.
|
||||
pub fn font_dir() -> Option<PathBuf> {
|
||||
dirs_next::font_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's home directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$HOME`.
|
||||
/// - **macOS:** Resolves to `$HOME`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Profile}`.
|
||||
pub fn home_dir() -> Option<PathBuf> {
|
||||
dirs_next::home_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's picture directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_PICTURES_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Pictures`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Pictures}`.
|
||||
pub fn picture_dir() -> Option<PathBuf> {
|
||||
dirs_next::picture_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's public directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_PUBLICSHARE_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Public`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Public}`.
|
||||
pub fn public_dir() -> Option<PathBuf> {
|
||||
dirs_next::public_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's runtime directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_RUNTIME_DIR`.
|
||||
/// - **macOS:** Not supported.
|
||||
/// - **Windows:** Not supported.
|
||||
pub fn runtime_dir() -> Option<PathBuf> {
|
||||
dirs_next::runtime_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's template directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_TEMPLATES_DIR`.
|
||||
/// - **macOS:** Not supported.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Templates}`.
|
||||
pub fn template_dir() -> Option<PathBuf> {
|
||||
dirs_next::template_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the user's video dir
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_VIDEOS_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Movies`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Videos}`.
|
||||
pub fn video_dir() -> Option<PathBuf> {
|
||||
dirs_next::video_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the resource directory of this app.
|
||||
///
|
||||
/// See [`PathResolver::resource_dir`](crate::PathResolver#method.resource_dir) for a more convenient helper function.
|
||||
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's config files.
|
||||
///
|
||||
/// Resolves to [`config_dir`]`/${bundle_identifier}`.
|
||||
///
|
||||
/// 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 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 local data files.
|
||||
///
|
||||
/// Resolves to [`local_data_dir`]`/${bundle_identifier}`.
|
||||
///
|
||||
/// See [`PathResolver::app_local_data_dir`](crate::PathResolver#method.app_local_data_dir) for a more convenient helper function.
|
||||
pub fn app_local_data_dir(config: &Config) -> Option<PathBuf> {
|
||||
dirs_next::data_local_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}/logs`.
|
||||
/// - **macOS:** Resolves to [`home_dir`]`/Library/Logs/${bundle_identifier}`
|
||||
/// - **Windows:** Resolves to [`config_dir`]`/${bundle_identifier}/logs`.
|
||||
///
|
||||
/// 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
|
||||
.join("Library/Logs")
|
||||
.join(&config.tauri.bundle.identifier)
|
||||
});
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let path =
|
||||
dirs_next::config_dir().map(|dir| dir.join(&config.tauri.bundle.identifier).join("logs"));
|
||||
|
||||
path
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's config files.
|
||||
///
|
||||
/// Resolves to [`config_dir`]`/${bundle_identifier}`.
|
||||
///
|
||||
/// See [`PathResolver::app_config_dir`](crate::PathResolver#method.app_config_dir) for a more convenient helper function.
|
||||
#[deprecated(
|
||||
since = "1.2.0",
|
||||
note = "Will be removed in 2.0.0. Use `app_config_dir` or `app_data_dir` instead."
|
||||
)]
|
||||
pub fn app_dir(config: &Config) -> Option<PathBuf> {
|
||||
app_config_dir(config)
|
||||
}
|
||||
|
||||
/// 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}`.
|
||||
///
|
||||
/// See [`PathResolver::app_log_dir`](crate::PathResolver#method.app_log_dir) for a more convenient helper function.
|
||||
#[deprecated(
|
||||
since = "1.2.0",
|
||||
note = "Will be removed in 2.0.0. Use `app_log_dir` instead."
|
||||
)]
|
||||
pub fn log_dir(config: &Config) -> Option<PathBuf> {
|
||||
app_log_dir(config)
|
||||
}
|
@ -22,7 +22,7 @@ use crate::{
|
||||
scope::FsScope,
|
||||
sealed::{ManagerBase, RuntimeOrDispatch},
|
||||
utils::config::Config,
|
||||
utils::{assets::Assets, resources::resource_relpath, Env},
|
||||
utils::{assets::Assets, Env},
|
||||
Context, DeviceEventFilter, EventLoopMessage, Invoke, InvokeError, InvokeResponse, Manager,
|
||||
Runtime, Scopes, StateManager, Theme, Window,
|
||||
};
|
||||
@ -41,7 +41,6 @@ use tauri_utils::PackageInfo;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
path::{Path, PathBuf},
|
||||
sync::{mpsc::Sender, Arc, Weak},
|
||||
};
|
||||
|
||||
@ -242,111 +241,6 @@ pub(crate) struct UpdaterSettings {
|
||||
pub(crate) target: Option<String>,
|
||||
}
|
||||
|
||||
/// The path resolver is a helper for the application-specific [`crate::api::path`] APIs.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PathResolver {
|
||||
env: Env,
|
||||
config: Arc<Config>,
|
||||
package_info: PackageInfo,
|
||||
}
|
||||
|
||||
impl PathResolver {
|
||||
/// Returns the path to the resource directory of this app.
|
||||
///
|
||||
/// Helper function for [`crate::api::path::resource_dir`].
|
||||
pub fn resource_dir(&self) -> Option<PathBuf> {
|
||||
crate::api::path::resource_dir(&self.package_info, &self.env)
|
||||
}
|
||||
|
||||
/// Resolves the path of the given resource.
|
||||
/// Note that the path must be the same as provided in `tauri.conf.json`.
|
||||
///
|
||||
/// This function is helpful when your resource path includes a root dir (`/`) or parent component (`..`),
|
||||
/// because Tauri replaces them with a parent folder, so simply using [`Self::resource_dir`] and joining the path
|
||||
/// won't work.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// `tauri.conf.json`:
|
||||
/// ```json
|
||||
/// {
|
||||
/// "tauri": {
|
||||
/// "bundle": {
|
||||
/// "resources": ["../assets/*"]
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let resource_path = app.path_resolver()
|
||||
/// .resolve_resource("../assets/logo.svg")
|
||||
/// .expect("failed to resolve resource dir");
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn resolve_resource<P: AsRef<Path>>(&self, path: P) -> Option<PathBuf> {
|
||||
self
|
||||
.resource_dir()
|
||||
.map(|dir| dir.join(resource_relpath(path.as_ref())))
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's config files.
|
||||
///
|
||||
/// Helper function for [`crate::api::path::app_config_dir`].
|
||||
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.
|
||||
///
|
||||
/// Helper function for [`crate::api::path::app_data_dir`].
|
||||
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 local data files.
|
||||
///
|
||||
/// Helper function for [`crate::api::path::app_local_data_dir`].
|
||||
pub fn app_local_data_dir(&self) -> Option<PathBuf> {
|
||||
crate::api::path::app_local_data_dir(&self.config)
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's cache files.
|
||||
///
|
||||
/// Helper function for [`crate::api::path::app_cache_dir`].
|
||||
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.
|
||||
///
|
||||
/// Helper function for [`crate::api::path::app_log_dir`].
|
||||
pub fn app_log_dir(&self) -> Option<PathBuf> {
|
||||
crate::api::path::app_log_dir(&self.config)
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's config files.
|
||||
#[deprecated(
|
||||
since = "1.2.0",
|
||||
note = "Will be removed in 2.0.0. Use `app_config_dir` or `app_data_dir` instead."
|
||||
)]
|
||||
pub fn app_dir(&self) -> Option<PathBuf> {
|
||||
self.app_config_dir()
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's log files.
|
||||
#[deprecated(
|
||||
since = "1.2.0",
|
||||
note = "Will be removed in 2.0.0. Use `app_log_dir` instead."
|
||||
)]
|
||||
pub fn log_dir(&self) -> Option<PathBuf> {
|
||||
self.app_log_dir()
|
||||
}
|
||||
}
|
||||
|
||||
/// The asset resolver is a helper to access the [`tauri_utils::assets::Assets`] interface.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AssetResolver<R: Runtime> {
|
||||
@ -733,15 +627,6 @@ macro_rules! shared_app_impl {
|
||||
.get_tray(id)
|
||||
}
|
||||
|
||||
/// The path resolver for the application.
|
||||
pub fn path_resolver(&self) -> PathResolver {
|
||||
PathResolver {
|
||||
env: self.state::<Env>().inner().clone(),
|
||||
config: self.manager.config(),
|
||||
package_info: self.manager.package_info().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a copy of the global shortcut manager instance.
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "global-shortcut")))]
|
||||
@ -802,6 +687,11 @@ shared_app_impl!(App<R>);
|
||||
shared_app_impl!(AppHandle<R>);
|
||||
|
||||
impl<R: Runtime> App<R> {
|
||||
fn register_core_plugins(&self) -> crate::Result<()> {
|
||||
self.handle.plugin(crate::path::init())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets a handle to the application instance.
|
||||
pub fn handle(&self) -> AppHandle<R> {
|
||||
self.handle.clone()
|
||||
@ -1663,27 +1553,23 @@ impl<R: Runtime> Builder<R> {
|
||||
},
|
||||
};
|
||||
|
||||
app.register_core_plugins()?;
|
||||
|
||||
let env = Env::default();
|
||||
app.manage(env);
|
||||
|
||||
app.manage(Scopes {
|
||||
fs: FsScope::for_fs_api(
|
||||
&app.manager.config(),
|
||||
app.package_info(),
|
||||
&env,
|
||||
&app.config().tauri.allowlist.fs.scope,
|
||||
)?,
|
||||
fs: FsScope::for_fs_api(&app, &app.config().tauri.allowlist.fs.scope)?,
|
||||
#[cfg(protocol_asset)]
|
||||
asset_protocol: FsScope::for_fs_api(
|
||||
&app.manager.config(),
|
||||
app.package_info(),
|
||||
&env,
|
||||
&app,
|
||||
&app.config().tauri.allowlist.protocol.asset_scope,
|
||||
)?,
|
||||
#[cfg(http_request)]
|
||||
http: crate::scope::HttpScope::for_http_api(&app.config().tauri.allowlist.http.scope),
|
||||
#[cfg(shell_scope)]
|
||||
shell: ShellScope::new(&app.manager.config(), app.package_info(), &env, shell_scope),
|
||||
shell: ShellScope::new(&app, shell_scope),
|
||||
});
|
||||
app.manage(env);
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
@ -1695,7 +1581,8 @@ impl<R: Runtime> Builder<R> {
|
||||
.windows
|
||||
.webview_install_mode
|
||||
{
|
||||
if let Some(resource_dir) = app.path_resolver().resource_dir() {
|
||||
use crate::path::PathExt;
|
||||
if let Ok(resource_dir) = app.path().resource_dir() {
|
||||
std::env::set_var(
|
||||
"WEBVIEW2_BROWSER_EXECUTABLE_FOLDER",
|
||||
resource_dir.join(path),
|
||||
@ -1874,8 +1761,5 @@ mod tests {
|
||||
crate::test_utils::assert_send::<super::AssetResolver<crate::Wry>>();
|
||||
crate::test_utils::assert_sync::<super::AssetResolver<crate::Wry>>();
|
||||
}
|
||||
|
||||
crate::test_utils::assert_send::<super::PathResolver>();
|
||||
crate::test_utils::assert_sync::<super::PathResolver>();
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,6 @@ mod http;
|
||||
mod notification;
|
||||
#[cfg(os_any)]
|
||||
mod operating_system;
|
||||
#[cfg(path_any)]
|
||||
mod path;
|
||||
#[cfg(process_any)]
|
||||
mod process;
|
||||
#[cfg(shell_any)]
|
||||
@ -78,8 +76,6 @@ enum Module {
|
||||
Fs(file_system::Cmd),
|
||||
#[cfg(os_any)]
|
||||
Os(operating_system::Cmd),
|
||||
#[cfg(path_any)]
|
||||
Path(path::Cmd),
|
||||
Window(Box<window::Cmd>),
|
||||
#[cfg(shell_any)]
|
||||
Shell(shell::Cmd),
|
||||
@ -131,13 +127,6 @@ impl Module {
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(path_any)]
|
||||
Self::Path(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(os_any)]
|
||||
Self::Os(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
|
@ -8,8 +8,8 @@ use crate::{
|
||||
api::{
|
||||
dir,
|
||||
file::{self, SafePathBuf},
|
||||
path::BaseDirectory,
|
||||
},
|
||||
path::{BaseDirectory, PathExt},
|
||||
scope::Scopes,
|
||||
Config, Env, Manager, PackageInfo, Runtime, Window,
|
||||
};
|
||||
@ -129,13 +129,7 @@ impl Cmd {
|
||||
path: SafePathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
) -> super::Result<Vec<u8>> {
|
||||
let resolved_path = resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
path,
|
||||
options.and_then(|o| o.dir),
|
||||
)?;
|
||||
let resolved_path = resolve_path(&context.window, path, options.and_then(|o| o.dir))?;
|
||||
file::read_binary(&resolved_path)
|
||||
.with_context(|| format!("path: {}", resolved_path.display()))
|
||||
.map_err(Into::into)
|
||||
@ -147,13 +141,7 @@ impl Cmd {
|
||||
path: SafePathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
) -> super::Result<String> {
|
||||
let resolved_path = resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
path,
|
||||
options.and_then(|o| o.dir),
|
||||
)?;
|
||||
let resolved_path = resolve_path(&context.window, path, options.and_then(|o| o.dir))?;
|
||||
file::read_string(&resolved_path)
|
||||
.with_context(|| format!("path: {}", resolved_path.display()))
|
||||
.map_err(Into::into)
|
||||
@ -166,13 +154,7 @@ impl Cmd {
|
||||
contents: Vec<u8>,
|
||||
options: Option<FileOperationOptions>,
|
||||
) -> super::Result<()> {
|
||||
let resolved_path = resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
path,
|
||||
options.and_then(|o| o.dir),
|
||||
)?;
|
||||
let resolved_path = resolve_path(&context.window, path, options.and_then(|o| o.dir))?;
|
||||
File::create(&resolved_path)
|
||||
.with_context(|| format!("path: {}", resolved_path.display()))
|
||||
.map_err(Into::into)
|
||||
@ -190,13 +172,7 @@ impl Cmd {
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
let resolved_path = resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
path,
|
||||
dir,
|
||||
)?;
|
||||
let resolved_path = resolve_path(&context.window, path, dir)?;
|
||||
dir::read_dir_with_options(
|
||||
&resolved_path,
|
||||
recursive,
|
||||
@ -217,20 +193,8 @@ impl Cmd {
|
||||
) -> super::Result<()> {
|
||||
let (src, dest) = match options.and_then(|o| o.dir) {
|
||||
Some(dir) => (
|
||||
resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
source,
|
||||
Some(dir),
|
||||
)?,
|
||||
resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
destination,
|
||||
Some(dir),
|
||||
)?,
|
||||
resolve_path(&context.window, source, Some(dir))?,
|
||||
resolve_path(&context.window, destination, Some(dir))?,
|
||||
),
|
||||
None => (source, destination),
|
||||
};
|
||||
@ -250,13 +214,7 @@ impl Cmd {
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
let resolved_path = resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
path,
|
||||
dir,
|
||||
)?;
|
||||
let resolved_path = resolve_path(&context.window, path, dir)?;
|
||||
if recursive {
|
||||
fs::create_dir_all(&resolved_path)
|
||||
.with_context(|| format!("path: {}", resolved_path.display()))?;
|
||||
@ -279,13 +237,7 @@ impl Cmd {
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
let resolved_path = resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
path,
|
||||
dir,
|
||||
)?;
|
||||
let resolved_path = resolve_path(&context.window, path, dir)?;
|
||||
if recursive {
|
||||
fs::remove_dir_all(&resolved_path)
|
||||
.with_context(|| format!("path: {}", resolved_path.display()))?;
|
||||
@ -303,13 +255,7 @@ impl Cmd {
|
||||
path: SafePathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
) -> super::Result<()> {
|
||||
let resolved_path = resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
path,
|
||||
options.and_then(|o| o.dir),
|
||||
)?;
|
||||
let resolved_path = resolve_path(&context.window, path, options.and_then(|o| o.dir))?;
|
||||
fs::remove_file(&resolved_path)
|
||||
.with_context(|| format!("path: {}", resolved_path.display()))?;
|
||||
Ok(())
|
||||
@ -324,20 +270,8 @@ impl Cmd {
|
||||
) -> super::Result<()> {
|
||||
let (old, new) = match options.and_then(|o| o.dir) {
|
||||
Some(dir) => (
|
||||
resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
old_path,
|
||||
Some(dir),
|
||||
)?,
|
||||
resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
new_path,
|
||||
Some(dir),
|
||||
)?,
|
||||
resolve_path(&context.window, old_path, Some(dir))?,
|
||||
resolve_path(&context.window, new_path, Some(dir))?,
|
||||
),
|
||||
None => (old_path, new_path),
|
||||
};
|
||||
@ -352,27 +286,22 @@ impl Cmd {
|
||||
path: SafePathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
) -> super::Result<bool> {
|
||||
let resolved_path = resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
&context.window,
|
||||
path,
|
||||
options.and_then(|o| o.dir),
|
||||
)?;
|
||||
let resolved_path = resolve_path(&context.window, path, options.and_then(|o| o.dir))?;
|
||||
Ok(resolved_path.as_ref().exists())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn resolve_path<R: Runtime>(
|
||||
config: &Config,
|
||||
package_info: &PackageInfo,
|
||||
window: &Window<R>,
|
||||
path: SafePathBuf,
|
||||
dir: Option<BaseDirectory>,
|
||||
) -> super::Result<SafePathBuf> {
|
||||
let env = window.state::<Env>().inner();
|
||||
match crate::api::path::resolve_path(config, package_info, env, &path, dir) {
|
||||
match if let Some(dir) = dir {
|
||||
window.path().resolve(&path, dir)
|
||||
} else {
|
||||
Ok(path.as_ref().to_path_buf())
|
||||
} {
|
||||
Ok(path) => {
|
||||
if window.state::<Scopes>().fs.is_allowed(&path) {
|
||||
Ok(
|
||||
|
@ -1,283 +0,0 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use crate::{api::path::BaseDirectory, Runtime};
|
||||
#[cfg(path_all)]
|
||||
use crate::{Env, Manager};
|
||||
use std::path::PathBuf;
|
||||
#[cfg(path_all)]
|
||||
use std::path::{Component, Path, MAIN_SEPARATOR};
|
||||
|
||||
use super::InvokeContext;
|
||||
use serde::Deserialize;
|
||||
use tauri_macros::{command_enum, module_command_handler, CommandModule};
|
||||
|
||||
/// The API descriptor.
|
||||
#[command_enum]
|
||||
#[derive(Deserialize, CommandModule)]
|
||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||
pub enum Cmd {
|
||||
#[cmd(path_all, "path > all")]
|
||||
ResolvePath {
|
||||
path: String,
|
||||
directory: Option<BaseDirectory>,
|
||||
},
|
||||
#[cmd(path_all, "path > all")]
|
||||
Resolve { paths: Vec<String> },
|
||||
#[cmd(path_all, "path > all")]
|
||||
Normalize { path: String },
|
||||
#[cmd(path_all, "path > all")]
|
||||
Join { paths: Vec<String> },
|
||||
#[cmd(path_all, "path > all")]
|
||||
Dirname { path: String },
|
||||
#[cmd(path_all, "path > all")]
|
||||
Extname { path: String },
|
||||
#[cmd(path_all, "path > all")]
|
||||
Basename { path: String, ext: Option<String> },
|
||||
#[cmd(path_all, "path > all")]
|
||||
IsAbsolute { path: String },
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
#[module_command_handler(path_all)]
|
||||
fn resolve_path<R: Runtime>(
|
||||
context: InvokeContext<R>,
|
||||
path: String,
|
||||
directory: Option<BaseDirectory>,
|
||||
) -> super::Result<PathBuf> {
|
||||
crate::api::path::resolve_path(
|
||||
&context.config,
|
||||
&context.package_info,
|
||||
context.window.state::<Env>().inner(),
|
||||
path,
|
||||
directory,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[module_command_handler(path_all)]
|
||||
fn resolve<R: Runtime>(_context: InvokeContext<R>, paths: Vec<String>) -> super::Result<PathBuf> {
|
||||
// Start with current directory then start adding paths from the vector one by one using `PathBuf.push()` which
|
||||
// will ensure that if an absolute path is encountered in the iteration, it will be used as the current full path.
|
||||
//
|
||||
// examples:
|
||||
// 1. `vec!["."]` or `vec![]` will be equal to `std::env::current_dir()`
|
||||
// 2. `vec!["/foo/bar", "/tmp/file", "baz"]` will be equal to `PathBuf::from("/tmp/file/baz")`
|
||||
let mut path = std::env::current_dir()?;
|
||||
for p in paths {
|
||||
path.push(p);
|
||||
}
|
||||
Ok(normalize_path(&path))
|
||||
}
|
||||
|
||||
#[module_command_handler(path_all)]
|
||||
fn normalize<R: Runtime>(_context: InvokeContext<R>, path: String) -> super::Result<String> {
|
||||
let mut p = normalize_path_no_absolute(Path::new(&path))
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
Ok(
|
||||
// Node.js behavior is to return `".."` for `normalize("..")`
|
||||
// and `"."` for `normalize("")` or `normalize(".")`
|
||||
if p.is_empty() && path == ".." {
|
||||
"..".into()
|
||||
} else if p.is_empty() && path == "." {
|
||||
".".into()
|
||||
} else {
|
||||
// Add a trailing separator if the path passed to this functions had a trailing separator. That's how Node.js behaves.
|
||||
if (path.ends_with('/') || path.ends_with('\\'))
|
||||
&& (!p.ends_with('/') || !p.ends_with('\\'))
|
||||
{
|
||||
p.push(MAIN_SEPARATOR);
|
||||
}
|
||||
p
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[module_command_handler(path_all)]
|
||||
fn join<R: Runtime>(_context: InvokeContext<R>, mut paths: Vec<String>) -> super::Result<String> {
|
||||
let path = PathBuf::from(
|
||||
paths
|
||||
.iter_mut()
|
||||
.map(|p| {
|
||||
// Add a `MAIN_SEPARATOR` if it doesn't already have one.
|
||||
// Doing this to ensure that the vector elements are separated in
|
||||
// the resulting string so path.components() can work correctly when called
|
||||
// in `normalize_path_no_absolute()` later on.
|
||||
if !p.ends_with('/') && !p.ends_with('\\') {
|
||||
p.push(MAIN_SEPARATOR);
|
||||
}
|
||||
p.to_string()
|
||||
})
|
||||
.collect::<String>(),
|
||||
);
|
||||
|
||||
let p = normalize_path_no_absolute(&path)
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
Ok(if p.is_empty() { ".".into() } else { p })
|
||||
}
|
||||
|
||||
#[module_command_handler(path_all)]
|
||||
fn dirname<R: Runtime>(_context: InvokeContext<R>, path: String) -> super::Result<PathBuf> {
|
||||
match Path::new(&path).parent() {
|
||||
Some(p) => Ok(p.to_path_buf()),
|
||||
None => Err(crate::error::into_anyhow(crate::api::Error::Path(
|
||||
"Couldn't get the parent directory".into(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[module_command_handler(path_all)]
|
||||
fn extname<R: Runtime>(_context: InvokeContext<R>, path: String) -> super::Result<String> {
|
||||
match Path::new(&path)
|
||||
.extension()
|
||||
.and_then(std::ffi::OsStr::to_str)
|
||||
{
|
||||
Some(p) => Ok(p.to_string()),
|
||||
None => Err(crate::error::into_anyhow(crate::api::Error::Path(
|
||||
"Couldn't get the extension of the file".into(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[module_command_handler(path_all)]
|
||||
fn basename<R: Runtime>(
|
||||
_context: InvokeContext<R>,
|
||||
path: String,
|
||||
ext: Option<String>,
|
||||
) -> super::Result<String> {
|
||||
match Path::new(&path)
|
||||
.file_name()
|
||||
.and_then(std::ffi::OsStr::to_str)
|
||||
{
|
||||
Some(p) => Ok(if let Some(ext) = ext {
|
||||
p.replace(ext.as_str(), "")
|
||||
} else {
|
||||
p.to_string()
|
||||
}),
|
||||
None => Err(crate::error::into_anyhow(crate::api::Error::Path(
|
||||
"Couldn't get the basename".into(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[module_command_handler(path_all)]
|
||||
fn is_absolute<R: Runtime>(_context: InvokeContext<R>, path: String) -> super::Result<bool> {
|
||||
Ok(Path::new(&path).is_absolute())
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalize a path, removing things like `.` and `..`, this snippet is taken from cargo's paths util.
|
||||
/// https://github.com/rust-lang/cargo/blob/46fa867ff7043e3a0545bf3def7be904e1497afd/crates/cargo-util/src/paths.rs#L73-L106
|
||||
#[cfg(path_all)]
|
||||
fn normalize_path(path: &Path) -> PathBuf {
|
||||
let mut components = path.components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
ret.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Normalize a path, removing things like `.` and `..`, this snippet is taken from cargo's paths util but
|
||||
/// slightly modified to not resolve absolute paths.
|
||||
/// https://github.com/rust-lang/cargo/blob/46fa867ff7043e3a0545bf3def7be904e1497afd/crates/cargo-util/src/paths.rs#L73-L106
|
||||
#[cfg(path_all)]
|
||||
fn normalize_path_no_absolute(path: &Path) -> PathBuf {
|
||||
let mut components = path.components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
// Using PathBuf::push here will replace the whole path if an absolute path is encountered
|
||||
// which is not the intended behavior, so instead of that, convert the current resolved path
|
||||
// to a string and do simple string concatenation with the current component then convert it
|
||||
// back to a PathBuf
|
||||
let mut p = ret.to_string_lossy().to_string();
|
||||
// Only add a separator if it doesn't have one already or if current normalized path is empty,
|
||||
// this ensures it won't have an unwanted leading separator
|
||||
if !p.is_empty() && !p.ends_with('/') && !p.ends_with('\\') {
|
||||
p.push(MAIN_SEPARATOR);
|
||||
}
|
||||
if let Some(c) = c.to_str() {
|
||||
p.push_str(c);
|
||||
}
|
||||
ret = PathBuf::from(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::path::BaseDirectory;
|
||||
|
||||
#[tauri_macros::module_command_test(path_all, "path > all")]
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn resolve_path(_path: String, _directory: Option<BaseDirectory>) {}
|
||||
|
||||
#[tauri_macros::module_command_test(path_all, "path > all")]
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn resolve(_paths: Vec<String>) {}
|
||||
|
||||
#[tauri_macros::module_command_test(path_all, "path > all")]
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn normalize(_path: String) {}
|
||||
|
||||
#[tauri_macros::module_command_test(path_all, "path > all")]
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn join(_paths: Vec<String>) {}
|
||||
|
||||
#[tauri_macros::module_command_test(path_all, "path > all")]
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn dirname(_path: String) {}
|
||||
|
||||
#[tauri_macros::module_command_test(path_all, "path > all")]
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn extname(_path: String) {}
|
||||
|
||||
#[tauri_macros::module_command_test(path_all, "path > all")]
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn basename(_path: String, _ext: Option<String>) {}
|
||||
|
||||
#[tauri_macros::module_command_test(path_all, "path > all")]
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn is_absolute(_path: String) {}
|
||||
}
|
@ -205,6 +205,8 @@ use tauri_runtime as runtime;
|
||||
mod ios;
|
||||
#[cfg(target_os = "android")]
|
||||
mod jni_helpers;
|
||||
/// Path APIs.
|
||||
pub mod path;
|
||||
/// The allowlist scopes.
|
||||
pub mod scope;
|
||||
mod state;
|
||||
@ -286,8 +288,8 @@ pub use {
|
||||
};
|
||||
pub use {
|
||||
self::app::{
|
||||
App, AppHandle, AssetResolver, Builder, CloseRequestApi, GlobalWindowEvent, PathResolver,
|
||||
RunEvent, WindowEvent,
|
||||
App, AppHandle, AssetResolver, Builder, CloseRequestApi, GlobalWindowEvent, RunEvent,
|
||||
WindowEvent,
|
||||
},
|
||||
self::hooks::{
|
||||
Invoke, InvokeError, InvokeHandler, InvokeMessage, InvokePayload, InvokeResolver,
|
||||
@ -690,6 +692,11 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
|
||||
self.manager().config()
|
||||
}
|
||||
|
||||
/// The [`PackageInfo`] the manager was created with.
|
||||
fn package_info(&self) -> &PackageInfo {
|
||||
self.manager().package_info()
|
||||
}
|
||||
|
||||
/// Emits a event to all windows.
|
||||
fn emit_all<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
|
||||
self.manager().emit_filter(event, None, payload, |_| true)
|
||||
|
@ -56,7 +56,7 @@ use crate::{
|
||||
};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
use crate::api::path::{resolve_path, BaseDirectory};
|
||||
use crate::path::{BaseDirectory, PathExt};
|
||||
|
||||
use crate::{runtime::menu::Menu, MenuEvent};
|
||||
|
||||
@ -1321,28 +1321,13 @@ impl<R: Runtime> WindowManager<R> {
|
||||
});
|
||||
}
|
||||
|
||||
if is_local {
|
||||
let label = pending.label.clone();
|
||||
pending = self.prepare_pending_window(
|
||||
pending,
|
||||
&label,
|
||||
window_labels,
|
||||
app_handle.clone(),
|
||||
web_resource_request_handler,
|
||||
)?;
|
||||
pending.ipc_handler = Some(self.prepare_ipc_handler(app_handle));
|
||||
}
|
||||
|
||||
// in `Windows`, we need to force a data_directory
|
||||
// but we do respect user-specification
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
if pending.webview_attributes.data_directory.is_none() {
|
||||
let local_app_data = resolve_path(
|
||||
&self.inner.config,
|
||||
&self.inner.package_info,
|
||||
self.inner.state.get::<crate::Env>().inner(),
|
||||
let local_app_data = app_handle.path().resolve(
|
||||
&self.inner.config.tauri.bundle.identifier,
|
||||
Some(BaseDirectory::LocalData),
|
||||
BaseDirectory::LocalData,
|
||||
);
|
||||
if let Ok(user_data_dir) = local_app_data {
|
||||
pending.webview_attributes.data_directory = Some(user_data_dir);
|
||||
@ -1356,6 +1341,18 @@ impl<R: Runtime> WindowManager<R> {
|
||||
}
|
||||
}
|
||||
|
||||
if is_local {
|
||||
let label = pending.label.clone();
|
||||
pending = self.prepare_pending_window(
|
||||
pending,
|
||||
&label,
|
||||
window_labels,
|
||||
app_handle.clone(),
|
||||
web_resource_request_handler,
|
||||
)?;
|
||||
pending.ipc_handler = Some(self.prepare_ipc_handler(app_handle));
|
||||
}
|
||||
|
||||
Ok(pending)
|
||||
}
|
||||
|
||||
|
115
core/tauri/src/path/android.rs
Normal file
115
core/tauri/src/path/android.rs
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::Result;
|
||||
use crate::{plugin::PluginHandle, Runtime};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// A helper class to access the mobile path APIs.
|
||||
pub struct PathResolver<R: Runtime>(pub(crate) PluginHandle<R>);
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct PathResponse {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl<R: Runtime> PathResolver<R> {
|
||||
fn call_resolve(&self, dir: &str) -> Result<PathBuf> {
|
||||
self
|
||||
.0
|
||||
.run_mobile_plugin::<PathResponse>(dir, ())
|
||||
.map(|r| r.path)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's audio directory.
|
||||
pub fn audio_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getAudioDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's cache directory.
|
||||
pub fn cache_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getExternalCacheDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's config directory.
|
||||
pub fn config_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getConfigDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's data directory.
|
||||
pub fn data_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getDataDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's local data directory.
|
||||
pub fn local_data_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getDataDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's document directory.
|
||||
pub fn document_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getDocumentDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's download directory.
|
||||
pub fn download_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getDownloadDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's picture directory.
|
||||
pub fn picture_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getPictureDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's public directory.
|
||||
pub fn public_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getPublicDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the user's video dir
|
||||
pub fn video_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getVideoDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the resource directory of this app.
|
||||
pub fn resource_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getResourcesDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's config files.
|
||||
///
|
||||
/// Resolves to [`config_dir`]`/${bundle_identifier}`.
|
||||
pub fn app_config_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getConfigDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's data files.
|
||||
///
|
||||
/// Resolves to [`data_dir`]`/${bundle_identifier}`.
|
||||
pub fn app_data_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getDataDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's local data files.
|
||||
///
|
||||
/// Resolves to [`local_data_dir`]`/${bundle_identifier}`.
|
||||
pub fn app_local_data_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getDataDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's cache files.
|
||||
///
|
||||
/// Resolves to [`cache_dir`]`/${bundle_identifier}`.
|
||||
pub fn app_cache_dir(&self) -> Result<PathBuf> {
|
||||
self.call_resolve("getCacheDir")
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's log files.
|
||||
pub fn app_log_dir(&self) -> Result<PathBuf> {
|
||||
self
|
||||
.call_resolve("getConfigDir")
|
||||
.map(|dir| dir.join("logs"))
|
||||
}
|
||||
}
|
193
core/tauri/src/path/commands.rs
Normal file
193
core/tauri/src/path/commands.rs
Normal file
@ -0,0 +1,193 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
|
||||
|
||||
use super::{BaseDirectory, Error, PathResolver, Result};
|
||||
use crate::{command, AppHandle, Runtime, State};
|
||||
|
||||
/// Normalize a path, removing things like `.` and `..`, this snippet is taken from cargo's paths util.
|
||||
/// https://github.com/rust-lang/cargo/blob/46fa867ff7043e3a0545bf3def7be904e1497afd/crates/cargo-util/src/paths.rs#L73-L106
|
||||
fn normalize_path(path: &Path) -> PathBuf {
|
||||
let mut components = path.components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
ret.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Normalize a path, removing things like `.` and `..`, this snippet is taken from cargo's paths util but
|
||||
/// slightly modified to not resolve absolute paths.
|
||||
/// https://github.com/rust-lang/cargo/blob/46fa867ff7043e3a0545bf3def7be904e1497afd/crates/cargo-util/src/paths.rs#L73-L106
|
||||
fn normalize_path_no_absolute(path: &Path) -> PathBuf {
|
||||
let mut components = path.components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
// Using PathBuf::push here will replace the whole path if an absolute path is encountered
|
||||
// which is not the intended behavior, so instead of that, convert the current resolved path
|
||||
// to a string and do simple string concatenation with the current component then convert it
|
||||
// back to a PathBuf
|
||||
let mut p = ret.to_string_lossy().to_string();
|
||||
// Only add a separator if it doesn't have one already or if current normalized path is empty,
|
||||
// this ensures it won't have an unwanted leading separator
|
||||
if !p.is_empty() && !p.ends_with('/') && !p.ends_with('\\') {
|
||||
p.push(MAIN_SEPARATOR);
|
||||
}
|
||||
if let Some(c) = c.to_str() {
|
||||
p.push_str(c);
|
||||
}
|
||||
ret = PathBuf::from(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub fn resolve_directory<R: Runtime>(
|
||||
_app: AppHandle<R>,
|
||||
resolver: State<'_, PathResolver<R>>,
|
||||
directory: BaseDirectory,
|
||||
path: Option<PathBuf>,
|
||||
) -> Result<PathBuf> {
|
||||
super::resolve_path(&resolver, directory, path)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub fn resolve(paths: Vec<String>) -> Result<PathBuf> {
|
||||
// Start with current directory then start adding paths from the vector one by one using `PathBuf.push()` which
|
||||
// will ensure that if an absolute path is encountered in the iteration, it will be used as the current full path.
|
||||
//
|
||||
// examples:
|
||||
// 1. `vec!["."]` or `vec![]` will be equal to `std::env::current_dir()`
|
||||
// 2. `vec!["/foo/bar", "/tmp/file", "baz"]` will be equal to `PathBuf::from("/tmp/file/baz")`
|
||||
let mut path = std::env::current_dir().map_err(Error::CurrentDir)?;
|
||||
for p in paths {
|
||||
path.push(p);
|
||||
}
|
||||
Ok(normalize_path(&path))
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub fn normalize(path: String) -> String {
|
||||
let mut p = normalize_path_no_absolute(Path::new(&path))
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
// Node.js behavior is to return `".."` for `normalize("..")`
|
||||
// and `"."` for `normalize("")` or `normalize(".")`
|
||||
if p.is_empty() && path == ".." {
|
||||
"..".into()
|
||||
} else if p.is_empty() && path == "." {
|
||||
".".into()
|
||||
} else {
|
||||
// Add a trailing separator if the path passed to this functions had a trailing separator. That's how Node.js behaves.
|
||||
if (path.ends_with('/') || path.ends_with('\\')) && (!p.ends_with('/') || !p.ends_with('\\')) {
|
||||
p.push(MAIN_SEPARATOR);
|
||||
}
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub fn join(mut paths: Vec<String>) -> String {
|
||||
let path = PathBuf::from(
|
||||
paths
|
||||
.iter_mut()
|
||||
.map(|p| {
|
||||
// Add a `MAIN_SEPARATOR` if it doesn't already have one.
|
||||
// Doing this to ensure that the vector elements are separated in
|
||||
// the resulting string so path.components() can work correctly when called
|
||||
// in `normalize_path_no_absolute()` later on.
|
||||
if !p.ends_with('/') && !p.ends_with('\\') {
|
||||
p.push(MAIN_SEPARATOR);
|
||||
}
|
||||
p.to_string()
|
||||
})
|
||||
.collect::<String>(),
|
||||
);
|
||||
|
||||
let p = normalize_path_no_absolute(&path)
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
if p.is_empty() {
|
||||
".".into()
|
||||
} else {
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub fn dirname(path: String) -> Result<PathBuf> {
|
||||
match Path::new(&path).parent() {
|
||||
Some(p) => Ok(p.to_path_buf()),
|
||||
None => Err(Error::NoParent),
|
||||
}
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub fn extname(path: String) -> Result<String> {
|
||||
match Path::new(&path)
|
||||
.extension()
|
||||
.and_then(std::ffi::OsStr::to_str)
|
||||
{
|
||||
Some(p) => Ok(p.to_string()),
|
||||
None => Err(Error::NoExtension),
|
||||
}
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub fn basename(path: String, ext: Option<String>) -> Result<String> {
|
||||
match Path::new(&path)
|
||||
.file_name()
|
||||
.and_then(std::ffi::OsStr::to_str)
|
||||
{
|
||||
Some(p) => Ok(if let Some(ext) = ext {
|
||||
p.replace(ext.as_str(), "")
|
||||
} else {
|
||||
p.to_string()
|
||||
}),
|
||||
None => Err(Error::NoBasename),
|
||||
}
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub fn is_absolute(path: String) -> bool {
|
||||
Path::new(&path).is_absolute()
|
||||
}
|
257
core/tauri/src/path/desktop.rs
Normal file
257
core/tauri/src/path/desktop.rs
Normal file
@ -0,0 +1,257 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::{Error, Result};
|
||||
use crate::{AppHandle, Manager, Runtime};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// A helper class to access the mobile camera APIs.
|
||||
pub struct PathResolver<R: Runtime>(pub(crate) AppHandle<R>);
|
||||
|
||||
impl<R: Runtime> PathResolver<R> {
|
||||
/// Returns the path to the user's audio directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_MUSIC_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Music`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Music}`.
|
||||
pub fn audio_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::audio_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's cache directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_CACHE_HOME` or `$HOME/.cache`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Caches`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_LocalAppData}`.
|
||||
pub fn cache_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::cache_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's config directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_CONFIG_HOME` or `$HOME/.config`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Application Support`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_RoamingAppData}`.
|
||||
pub fn config_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::config_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's data directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_DATA_HOME` or `$HOME/.local/share`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Application Support`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_RoamingAppData}`.
|
||||
pub fn data_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::data_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's local data directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_DATA_HOME` or `$HOME/.local/share`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Application Support`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_LocalAppData}`.
|
||||
pub fn local_data_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::data_local_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's desktop directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_DESKTOP_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Desktop`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Desktop}`.
|
||||
pub fn desktop_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::desktop_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's document directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_DOCUMENTS_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Documents`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Documents}`.
|
||||
pub fn document_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::document_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's download directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_DOWNLOAD_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Downloads`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Downloads}`.
|
||||
pub fn download_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::download_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's executable directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_BIN_HOME/../bin` or `$XDG_DATA_HOME/../bin` or `$HOME/.local/bin`.
|
||||
/// - **macOS:** Not supported.
|
||||
/// - **Windows:** Not supported.
|
||||
pub fn executable_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::executable_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's font directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_DATA_HOME/fonts` or `$HOME/.local/share/fonts`.
|
||||
/// - **macOS:** Resolves to `$HOME/Library/Fonts`.
|
||||
/// - **Windows:** Not supported.
|
||||
pub fn font_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::font_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's home directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$HOME`.
|
||||
/// - **macOS:** Resolves to `$HOME`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Profile}`.
|
||||
pub fn home_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::home_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's picture directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_PICTURES_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Pictures`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Pictures}`.
|
||||
pub fn picture_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::picture_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's public directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_PUBLICSHARE_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Public`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Public}`.
|
||||
pub fn public_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::public_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's runtime directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to `$XDG_RUNTIME_DIR`.
|
||||
/// - **macOS:** Not supported.
|
||||
/// - **Windows:** Not supported.
|
||||
pub fn runtime_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::runtime_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's template directory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_TEMPLATES_DIR`.
|
||||
/// - **macOS:** Not supported.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Templates}`.
|
||||
pub fn template_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::template_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the user's video dir
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Resolves to [`xdg-user-dirs`](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' `XDG_VIDEOS_DIR`.
|
||||
/// - **macOS:** Resolves to `$HOME/Movies`.
|
||||
/// - **Windows:** Resolves to `{FOLDERID_Videos}`.
|
||||
pub fn video_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::video_dir().ok_or(Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the resource directory of this app.
|
||||
pub fn resource_dir(&self) -> Result<PathBuf> {
|
||||
crate::utils::platform::resource_dir(self.0.package_info(), &self.0.env())
|
||||
.map_err(|_| Error::UnknownPath)
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's config files.
|
||||
///
|
||||
/// Resolves to [`config_dir`]`/${bundle_identifier}`.
|
||||
pub fn app_config_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::config_dir()
|
||||
.ok_or(Error::UnknownPath)
|
||||
.map(|dir| dir.join(&self.0.config().tauri.bundle.identifier))
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's data files.
|
||||
///
|
||||
/// Resolves to [`data_dir`]`/${bundle_identifier}`.
|
||||
pub fn app_data_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::data_dir()
|
||||
.ok_or(Error::UnknownPath)
|
||||
.map(|dir| dir.join(&self.0.config().tauri.bundle.identifier))
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's local data files.
|
||||
///
|
||||
/// Resolves to [`local_data_dir`]`/${bundle_identifier}`.
|
||||
pub fn app_local_data_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::data_local_dir()
|
||||
.ok_or(Error::UnknownPath)
|
||||
.map(|dir| dir.join(&self.0.config().tauri.bundle.identifier))
|
||||
}
|
||||
|
||||
/// Returns the path to the suggested directory for your app's cache files.
|
||||
///
|
||||
/// Resolves to [`cache_dir`]`/${bundle_identifier}`.
|
||||
pub fn app_cache_dir(&self) -> Result<PathBuf> {
|
||||
dirs_next::cache_dir()
|
||||
.ok_or(Error::UnknownPath)
|
||||
.map(|dir| dir.join(&self.0.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}/logs`.
|
||||
/// - **macOS:** Resolves to [`home_dir`]`/Library/Logs/${bundle_identifier}`
|
||||
/// - **Windows:** Resolves to [`config_dir`]`/${bundle_identifier}/logs`.
|
||||
pub fn app_log_dir(&self) -> Result<PathBuf> {
|
||||
#[cfg(target_os = "macos")]
|
||||
let path = dirs_next::home_dir().ok_or(Error::UnknownPath).map(|dir| {
|
||||
dir
|
||||
.join("Library/Logs")
|
||||
.join(&self.0.config().tauri.bundle.identifier)
|
||||
});
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let path = dirs_next::config_dir()
|
||||
.ok_or(Error::UnknownPath)
|
||||
.map(|dir| {
|
||||
dir
|
||||
.join(&self.0.config().tauri.bundle.identifier)
|
||||
.join("logs")
|
||||
});
|
||||
|
||||
path
|
||||
}
|
||||
}
|
42
core/tauri/src/path/error.rs
Normal file
42
core/tauri/src/path/error.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use serde::{ser::Serializer, Serialize};
|
||||
|
||||
/// Path result.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Path error.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// Path does not have a parent.
|
||||
#[error("path does not have a parent")]
|
||||
NoParent,
|
||||
/// Path does not have an extension.
|
||||
#[error("path does not have an extension")]
|
||||
NoExtension,
|
||||
/// Path does not have a basename.
|
||||
#[error("path does not have a basename")]
|
||||
NoBasename,
|
||||
/// Cannot resolve current directory.
|
||||
#[error("failed to read current dir: {0}")]
|
||||
CurrentDir(std::io::Error),
|
||||
/// Unknown path.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[error("unknown path")]
|
||||
UnknownPath,
|
||||
/// Failed to invoke mobile plugin.
|
||||
#[cfg(target_os = "android")]
|
||||
#[error(transparent)]
|
||||
PluginInvoke(#[from] crate::plugin::mobile::PluginInvokeError),
|
||||
}
|
||||
|
||||
impl Serialize for Error {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.to_string().as_ref())
|
||||
}
|
||||
}
|
346
core/tauri/src/path/mod.rs
Normal file
346
core/tauri/src/path/mod.rs
Normal file
@ -0,0 +1,346 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
env::temp_dir,
|
||||
path::{Component, Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
plugin::{Builder, TauriPlugin},
|
||||
Manager, Runtime,
|
||||
};
|
||||
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
#[cfg(path_all)]
|
||||
mod commands;
|
||||
mod error;
|
||||
pub use error::*;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
mod android;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
mod desktop;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
use android::PathResolver;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use desktop::PathResolver;
|
||||
|
||||
/// A base directory to be used in [`resolve_directory`].
|
||||
///
|
||||
/// The base directory is the optional root of a file system operation.
|
||||
/// If informed by the API call, all paths will be relative to the path of the given directory.
|
||||
///
|
||||
/// For more information, check the [`dirs_next` documentation](https://docs.rs/dirs_next/).
|
||||
#[derive(Serialize_repr, Deserialize_repr, Clone, Copy, Debug)]
|
||||
#[repr(u16)]
|
||||
#[non_exhaustive]
|
||||
pub enum BaseDirectory {
|
||||
/// The Audio directory.
|
||||
Audio = 1,
|
||||
/// The Cache directory.
|
||||
Cache,
|
||||
/// The Config directory.
|
||||
Config,
|
||||
/// The Data directory.
|
||||
Data,
|
||||
/// The LocalData directory.
|
||||
LocalData,
|
||||
/// The Document directory.
|
||||
Document,
|
||||
/// The Download directory.
|
||||
Download,
|
||||
/// The Picture directory.
|
||||
Picture,
|
||||
/// The Public directory.
|
||||
Public,
|
||||
/// The Video directory.
|
||||
Video,
|
||||
/// The Resource directory.
|
||||
Resource,
|
||||
/// 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 local data directory.
|
||||
/// Resolves to [`BaseDirectory::LocalData`]`/{bundle_identifier}`.
|
||||
AppLocalData,
|
||||
/// 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,
|
||||
|
||||
/// The Desktop directory.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Desktop,
|
||||
/// The Executable directory.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Executable,
|
||||
/// The Font directory.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Font,
|
||||
/// The Home directory.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Home,
|
||||
/// The Runtime directory.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Runtime,
|
||||
/// The Template directory.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Template,
|
||||
}
|
||||
|
||||
impl BaseDirectory {
|
||||
/// Gets the variable that represents this [`BaseDirectory`] for string paths.
|
||||
pub fn variable(self) -> &'static str {
|
||||
match self {
|
||||
Self::Audio => "$AUDIO",
|
||||
Self::Cache => "$CACHE",
|
||||
Self::Config => "$CONFIG",
|
||||
Self::Data => "$DATA",
|
||||
Self::LocalData => "$LOCALDATA",
|
||||
Self::Document => "$DOCUMENT",
|
||||
Self::Download => "$DOWNLOAD",
|
||||
Self::Picture => "$PICTURE",
|
||||
Self::Public => "$PUBLIC",
|
||||
Self::Video => "$VIDEO",
|
||||
Self::Resource => "$RESOURCE",
|
||||
Self::Temp => "$TEMP",
|
||||
Self::AppConfig => "$APPCONFIG",
|
||||
Self::AppData => "$APPDATA",
|
||||
Self::AppLocalData => "$APPLOCALDATA",
|
||||
Self::AppCache => "$APPCACHE",
|
||||
Self::AppLog => "$APPLOG",
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Self::Desktop => "$DESKTOP",
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Self::Executable => "$EXE",
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Self::Font => "$FONT",
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Self::Home => "$HOME",
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Self::Runtime => "$RUNTIME",
|
||||
#[cfg(not(target_os = "android"))]
|
||||
Self::Template => "$TEMPLATE",
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the [`BaseDirectory`] associated with the given variable, or [`None`] if the variable doesn't match any.
|
||||
pub fn from_variable(variable: &str) -> Option<Self> {
|
||||
let res = match variable {
|
||||
"$AUDIO" => Self::Audio,
|
||||
"$CACHE" => Self::Cache,
|
||||
"$CONFIG" => Self::Config,
|
||||
"$DATA" => Self::Data,
|
||||
"$LOCALDATA" => Self::LocalData,
|
||||
"$DOCUMENT" => Self::Document,
|
||||
"$DOWNLOAD" => Self::Download,
|
||||
|
||||
"$PICTURE" => Self::Picture,
|
||||
"$PUBLIC" => Self::Public,
|
||||
"$VIDEO" => Self::Video,
|
||||
"$RESOURCE" => Self::Resource,
|
||||
"$TEMP" => Self::Temp,
|
||||
"$APPCONFIG" => Self::AppConfig,
|
||||
"$APPDATA" => Self::AppData,
|
||||
"$APPLOCALDATA" => Self::AppLocalData,
|
||||
"$APPCACHE" => Self::AppCache,
|
||||
"$APPLOG" => Self::AppLog,
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
"$DESKTOP" => Self::Desktop,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
"$EXE" => Self::Executable,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
"$FONT" => Self::Font,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
"$HOME" => Self::Home,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
"$RUNTIME" => Self::Runtime,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
"$TEMPLATE" => Self::Template,
|
||||
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the path APIs.
|
||||
pub trait PathExt<R: Runtime> {
|
||||
/// The path resolver.
|
||||
fn path(&self) -> &PathResolver<R>;
|
||||
}
|
||||
|
||||
impl<R: Runtime, T: Manager<R>> PathExt<R> for T {
|
||||
fn path(&self) -> &PathResolver<R> {
|
||||
self.state::<PathResolver<R>>().inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> PathResolver<R> {
|
||||
/// Resolves the path with the base directory.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use tauri::{path::{BaseDirectory, PathExt}, Manager};
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let path = app.path().resolve("path/to/something", BaseDirectory::Config)?;
|
||||
/// assert_eq!(path.to_str().unwrap(), "/home/${whoami}/.config/path/to/something");
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn resolve<P: AsRef<Path>>(&self, path: P, base_directory: BaseDirectory) -> Result<PathBuf> {
|
||||
resolve_path::<R>(self, base_directory, Some(path.as_ref().to_path_buf()))
|
||||
}
|
||||
|
||||
/// Parse the given path, resolving a [`BaseDirectory`] variable if the path starts with one.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use tauri::{Manager, path::PathExt};
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let path = app.path().parse("$HOME/.bashrc")?;
|
||||
/// assert_eq!(path.to_str().unwrap(), "/home/${whoami}/.bashrc");
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn parse<P: AsRef<Path>>(&self, path: P) -> Result<PathBuf> {
|
||||
let mut p = PathBuf::new();
|
||||
let mut components = path.as_ref().components();
|
||||
match components.next() {
|
||||
Some(Component::Normal(str)) => {
|
||||
if let Some(base_directory) = BaseDirectory::from_variable(&str.to_string_lossy()) {
|
||||
p.push(resolve_path::<R>(self, base_directory, None)?);
|
||||
} else {
|
||||
p.push(str);
|
||||
}
|
||||
}
|
||||
Some(component) => p.push(component),
|
||||
None => (),
|
||||
}
|
||||
|
||||
for component in components {
|
||||
if let Component::ParentDir = component {
|
||||
continue;
|
||||
}
|
||||
p.push(component);
|
||||
}
|
||||
|
||||
Ok(p)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_path<R: Runtime>(
|
||||
resolver: &PathResolver<R>,
|
||||
directory: BaseDirectory,
|
||||
path: Option<PathBuf>,
|
||||
) -> Result<PathBuf> {
|
||||
let resolve_resource = matches!(directory, BaseDirectory::Resource);
|
||||
let mut base_dir_path = match directory {
|
||||
BaseDirectory::Audio => resolver.audio_dir(),
|
||||
BaseDirectory::Cache => resolver.cache_dir(),
|
||||
BaseDirectory::Config => resolver.config_dir(),
|
||||
BaseDirectory::Data => resolver.data_dir(),
|
||||
BaseDirectory::LocalData => resolver.local_data_dir(),
|
||||
BaseDirectory::Document => resolver.document_dir(),
|
||||
BaseDirectory::Download => resolver.download_dir(),
|
||||
BaseDirectory::Picture => resolver.picture_dir(),
|
||||
BaseDirectory::Public => resolver.public_dir(),
|
||||
BaseDirectory::Video => resolver.video_dir(),
|
||||
BaseDirectory::Resource => resolver.resource_dir(),
|
||||
BaseDirectory::Temp => Ok(temp_dir()),
|
||||
BaseDirectory::AppConfig => resolver.app_config_dir(),
|
||||
BaseDirectory::AppData => resolver.app_data_dir(),
|
||||
BaseDirectory::AppLocalData => resolver.app_local_data_dir(),
|
||||
BaseDirectory::AppCache => resolver.app_cache_dir(),
|
||||
BaseDirectory::AppLog => resolver.app_log_dir(),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
BaseDirectory::Desktop => resolver.desktop_dir(),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
BaseDirectory::Executable => resolver.executable_dir(),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
BaseDirectory::Font => resolver.font_dir(),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
BaseDirectory::Home => resolver.home_dir(),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
BaseDirectory::Runtime => resolver.runtime_dir(),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
BaseDirectory::Template => resolver.template_dir(),
|
||||
}?;
|
||||
|
||||
if let Some(path) = path {
|
||||
// use the same path resolution mechanism as the bundler's resource injection algorithm
|
||||
if resolve_resource {
|
||||
let mut resource_path = PathBuf::new();
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::Prefix(_) => {}
|
||||
Component::RootDir => resource_path.push("_root_"),
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => resource_path.push("_up_"),
|
||||
Component::Normal(p) => resource_path.push(p),
|
||||
}
|
||||
}
|
||||
base_dir_path.push(resource_path);
|
||||
} else {
|
||||
base_dir_path.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(base_dir_path)
|
||||
}
|
||||
|
||||
/// Initializes the plugin.
|
||||
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
#[allow(unused_mut)]
|
||||
let mut builder = Builder::new("path");
|
||||
|
||||
#[cfg(path_all)]
|
||||
{
|
||||
builder = builder.invoke_handler(crate::generate_handler![
|
||||
commands::resolve_directory,
|
||||
commands::resolve,
|
||||
commands::normalize,
|
||||
commands::join,
|
||||
commands::dirname,
|
||||
commands::extname,
|
||||
commands::basename,
|
||||
commands::is_absolute
|
||||
]);
|
||||
}
|
||||
|
||||
builder
|
||||
.setup(|app, _api| {
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
let handle = _api.register_android_plugin("app.tauri", "PathPlugin")?;
|
||||
app.manage(PathResolver(handle));
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
{
|
||||
app.manage(PathResolver(app.clone()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.build()
|
||||
}
|
@ -10,13 +10,10 @@ use std::{
|
||||
};
|
||||
|
||||
pub use glob::Pattern;
|
||||
use tauri_utils::{
|
||||
config::{Config, FsAllowlistScope},
|
||||
Env, PackageInfo,
|
||||
};
|
||||
use tauri_utils::config::FsAllowlistScope;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::api::path::parse as parse_path;
|
||||
use crate::{path::PathExt, Manager, Runtime};
|
||||
|
||||
/// Scope change event.
|
||||
#[derive(Debug, Clone)]
|
||||
@ -84,15 +81,13 @@ fn push_pattern<P: AsRef<Path>, F: Fn(&str) -> Result<Pattern, glob::PatternErro
|
||||
|
||||
impl Scope {
|
||||
/// Creates a new scope from a `FsAllowlistScope` configuration.
|
||||
pub(crate) fn for_fs_api(
|
||||
config: &Config,
|
||||
package_info: &PackageInfo,
|
||||
env: &Env,
|
||||
pub(crate) fn for_fs_api<R: Runtime, M: Manager<R>>(
|
||||
manager: &M,
|
||||
scope: &FsAllowlistScope,
|
||||
) -> crate::Result<Self> {
|
||||
let mut allowed_patterns = HashSet::new();
|
||||
for path in scope.allowed_paths() {
|
||||
if let Ok(path) = parse_path(config, package_info, env, path) {
|
||||
if let Ok(path) = manager.path().parse(path) {
|
||||
push_pattern(&mut allowed_patterns, path, Pattern::new)?;
|
||||
}
|
||||
}
|
||||
@ -100,7 +95,7 @@ impl Scope {
|
||||
let mut forbidden_patterns = HashSet::new();
|
||||
if let Some(forbidden_paths) = scope.forbidden_paths() {
|
||||
for path in forbidden_paths {
|
||||
if let Ok(path) = parse_path(config, package_info, env, path) {
|
||||
if let Ok(path) = manager.path().parse(path) {
|
||||
push_pattern(&mut forbidden_patterns, path, Pattern::new)?;
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
use crate::api::process::Command;
|
||||
#[cfg(feature = "shell-open-api")]
|
||||
use crate::api::shell::Program;
|
||||
use crate::{path::PathExt, Manager, Runtime};
|
||||
|
||||
use regex::Regex;
|
||||
use tauri_utils::{config::Config, Env, PackageInfo};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -194,14 +194,9 @@ pub enum ScopeError {
|
||||
|
||||
impl Scope {
|
||||
/// Creates a new shell scope.
|
||||
pub(crate) fn new(
|
||||
config: &Config,
|
||||
package_info: &PackageInfo,
|
||||
env: &Env,
|
||||
mut scope: ScopeConfig,
|
||||
) -> Self {
|
||||
pub(crate) fn new<R: Runtime, M: Manager<R>>(manager: &M, mut scope: ScopeConfig) -> Self {
|
||||
for cmd in scope.scopes.values_mut() {
|
||||
if let Ok(path) = crate::api::path::parse(config, package_info, env, &cmd.command) {
|
||||
if let Ok(path) = manager.path().parse(&cmd.command) {
|
||||
cmd.command = path;
|
||||
}
|
||||
}
|
||||
|
87
examples/api/src-tauri/Cargo.lock
generated
87
examples/api/src-tauri/Cargo.lock
generated
@ -67,24 +67,6 @@ dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e"
|
||||
|
||||
[[package]]
|
||||
name = "android_logger"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8619b80c242aa7bd638b5c7ddd952addeecb71f69c75e33f1d47b2804f8f883a"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"env_logger",
|
||||
"log",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.69"
|
||||
@ -100,7 +82,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-log",
|
||||
"tauri-plugin-sample",
|
||||
"tiny_http",
|
||||
"window-shadows",
|
||||
@ -347,16 +328,6 @@ version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "byte-unit"
|
||||
version = "4.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3348673602e04848647fffaa8e9a861e7b5d5cae6570727b41bde0f722514484"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"utf8-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.13.0"
|
||||
@ -861,16 +832,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.0"
|
||||
@ -907,15 +868,6 @@ dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "field-offset"
|
||||
version = "0.3.4"
|
||||
@ -1833,7 +1785,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3170,9 +3121,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "swift-rs"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eab55f44d02ced20ffa22fd5b4bd083ab59c940c7637d37fec4426b1a01d769"
|
||||
checksum = "8fa67d647176dfa7bdc5775430a1cb339e0ea48fe24707424023a4b17eb9688e"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"serde",
|
||||
@ -3397,24 +3348,6 @@ dependencies = [
|
||||
"tauri-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-log"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=next#39b144df42e6f3e485beef4d0d31101f13d4c24e"
|
||||
dependencies = [
|
||||
"android_logger",
|
||||
"byte-unit",
|
||||
"fern",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"swift-rs",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-sample"
|
||||
version = "0.1.0"
|
||||
@ -3871,12 +3804,6 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-width"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.0"
|
||||
@ -3892,16 +3819,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.0.0-alpha.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.1.1"
|
||||
|
@ -17,7 +17,7 @@ serde_json = "1.0"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
tiny_http = "0.11"
|
||||
log = "0.4"
|
||||
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "next" }
|
||||
#tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "next" }
|
||||
tauri-plugin-sample = { path = "./tauri-plugin-sample/" }
|
||||
|
||||
[patch.crates-io]
|
||||
|
@ -27,11 +27,11 @@ pub type OnEvent = Box<dyn FnMut(&AppHandle, RunEvent)>;
|
||||
pub fn run() {
|
||||
#[allow(unused_mut)]
|
||||
let mut builder = tauri::Builder::default()
|
||||
.plugin(
|
||||
tauri_plugin_log::Builder::default()
|
||||
/*.plugin(
|
||||
tauri_plugin_log::Builder::default()
|
||||
.level(log::LevelFilter::Info)
|
||||
.build(),
|
||||
)
|
||||
)*/
|
||||
.plugin(tauri_plugin_sample::init())
|
||||
.setup(move |app| {
|
||||
#[cfg(desktop)]
|
||||
|
File diff suppressed because one or more lines are too long
@ -62,8 +62,8 @@
|
||||
* {@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`},
|
||||
* {@link path.pictureDir | `$PICTURE`}, {@link path.publicDir | `$PUBLIC`}, {@link path.runtimeDir | `$RUNTIME`},
|
||||
* {@link path.templateDir | `$TEMPLATE`}, {@link path.videoDir | `$VIDEO`}, {@link path.resourceDir | `$RESOURCE`}, {@link path.appDir | `$APP`},
|
||||
* {@link path.logDir | `$LOG`}, {@link os.tempdir | `$TEMP`}.
|
||||
* {@link path.templateDir | `$TEMPLATE`}, {@link path.videoDir | `$VIDEO`}, {@link path.resourceDir | `$RESOURCE`},
|
||||
* {@link os.tempdir | `$TEMP`}.
|
||||
*
|
||||
* Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
|
||||
*
|
||||
@ -83,26 +83,25 @@ export enum BaseDirectory {
|
||||
Config,
|
||||
Data,
|
||||
LocalData,
|
||||
Desktop,
|
||||
Document,
|
||||
Download,
|
||||
Executable,
|
||||
Font,
|
||||
Home,
|
||||
Picture,
|
||||
Public,
|
||||
Runtime,
|
||||
Template,
|
||||
Video,
|
||||
Resource,
|
||||
App,
|
||||
Log,
|
||||
Temp,
|
||||
AppConfig,
|
||||
AppData,
|
||||
AppLocalData,
|
||||
AppCache,
|
||||
AppLog
|
||||
AppLog,
|
||||
|
||||
Desktop,
|
||||
Executable,
|
||||
Font,
|
||||
Home,
|
||||
Runtime,
|
||||
Template
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,20 +23,10 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { invokeTauriCommand } from './helpers/tauri'
|
||||
import { invoke } from './tauri'
|
||||
import { BaseDirectory } from './fs'
|
||||
import { isWindows } from './helpers/os-check'
|
||||
|
||||
/**
|
||||
* Returns the path to the suggested directory for your app config files.
|
||||
*
|
||||
* @deprecated since 1.2.0: Will be removed in 2.0.0. Use {@link appConfigDir} or {@link appDataDir} instead.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function appDir(): Promise<string> {
|
||||
return appConfigDir()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the suggested directory for your app's config files.
|
||||
* Resolves to `${configDir}/${bundleIdentifier}`, where `bundleIdentifier` is the value [`tauri.bundle.identifier`](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in `tauri.conf.json`.
|
||||
@ -49,13 +39,8 @@ async function appDir(): Promise<string> {
|
||||
* @since 1.2.0
|
||||
*/
|
||||
async function appConfigDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.AppConfig
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.AppConfig
|
||||
})
|
||||
}
|
||||
|
||||
@ -71,13 +56,8 @@ async function appConfigDir(): Promise<string> {
|
||||
* @since 1.2.0
|
||||
*/
|
||||
async function appDataDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.AppData
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.AppData
|
||||
})
|
||||
}
|
||||
|
||||
@ -93,13 +73,8 @@ async function appDataDir(): Promise<string> {
|
||||
* @since 1.2.0
|
||||
*/
|
||||
async function appLocalDataDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.AppLocalData
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.AppLocalData
|
||||
})
|
||||
}
|
||||
|
||||
@ -115,13 +90,8 @@ async function appLocalDataDir(): Promise<string> {
|
||||
* @since 1.2.0
|
||||
*/
|
||||
async function appCacheDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.AppCache
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.AppCache
|
||||
})
|
||||
}
|
||||
|
||||
@ -142,13 +112,8 @@ async function appCacheDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function audioDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Audio
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Audio
|
||||
})
|
||||
}
|
||||
|
||||
@ -169,13 +134,8 @@ async function audioDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function cacheDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Cache
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Cache
|
||||
})
|
||||
}
|
||||
|
||||
@ -196,13 +156,8 @@ async function cacheDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function configDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Config
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Config
|
||||
})
|
||||
}
|
||||
|
||||
@ -223,13 +178,8 @@ async function configDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function dataDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Data
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Data
|
||||
})
|
||||
}
|
||||
|
||||
@ -250,13 +200,8 @@ async function dataDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function desktopDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Desktop
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Desktop
|
||||
})
|
||||
}
|
||||
|
||||
@ -277,13 +222,8 @@ async function desktopDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function documentDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Document
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Document
|
||||
})
|
||||
}
|
||||
|
||||
@ -304,13 +244,8 @@ async function documentDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function downloadDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Download
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Download
|
||||
})
|
||||
}
|
||||
|
||||
@ -331,13 +266,8 @@ async function downloadDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function executableDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Executable
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Executable
|
||||
})
|
||||
}
|
||||
|
||||
@ -358,13 +288,8 @@ async function executableDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function fontDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Font
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Font
|
||||
})
|
||||
}
|
||||
|
||||
@ -385,13 +310,8 @@ async function fontDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function homeDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Home
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Home
|
||||
})
|
||||
}
|
||||
|
||||
@ -412,13 +332,8 @@ async function homeDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function localDataDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.LocalData
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.LocalData
|
||||
})
|
||||
}
|
||||
|
||||
@ -439,13 +354,8 @@ async function localDataDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function pictureDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Picture
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Picture
|
||||
})
|
||||
}
|
||||
|
||||
@ -466,13 +376,8 @@ async function pictureDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function publicDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Public
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Public
|
||||
})
|
||||
}
|
||||
|
||||
@ -488,13 +393,8 @@ async function publicDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function resourceDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Resource
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Resource
|
||||
})
|
||||
}
|
||||
|
||||
@ -513,13 +413,9 @@ async function resourceDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function resolveResource(resourcePath: string): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: resourcePath,
|
||||
directory: BaseDirectory.Resource
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Resource,
|
||||
path: resourcePath
|
||||
})
|
||||
}
|
||||
|
||||
@ -540,13 +436,8 @@ async function resolveResource(resourcePath: string): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function runtimeDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Runtime
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Runtime
|
||||
})
|
||||
}
|
||||
|
||||
@ -567,13 +458,8 @@ async function runtimeDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function templateDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Template
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Template
|
||||
})
|
||||
}
|
||||
|
||||
@ -594,26 +480,11 @@ async function templateDir(): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function videoDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Video
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Video
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the suggested log directory.
|
||||
*
|
||||
* @deprecated since 1.2.0: Will be removed in 2.0.0. Use {@link appLogDir} instead.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function logDir(): Promise<string> {
|
||||
return appLogDir()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the suggested directory for your app's log files.
|
||||
*
|
||||
@ -631,13 +502,8 @@ async function logDir(): Promise<string> {
|
||||
* @since 1.2.0
|
||||
*/
|
||||
async function appLogDir(): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.AppLog
|
||||
}
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.AppLog
|
||||
})
|
||||
}
|
||||
|
||||
@ -671,13 +537,7 @@ const delimiter = isWindows() ? ';' : ':'
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function resolve(...paths: string[]): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'resolve',
|
||||
paths
|
||||
}
|
||||
})
|
||||
return invoke('plugin:path|resolve', { paths })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -692,13 +552,7 @@ async function resolve(...paths: string[]): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function normalize(path: string): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'normalize',
|
||||
path
|
||||
}
|
||||
})
|
||||
return invoke('plugin:path|normalize', { path })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -713,13 +567,7 @@ async function normalize(path: string): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function join(...paths: string[]): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'join',
|
||||
paths
|
||||
}
|
||||
})
|
||||
return invoke('plugin:path|join', { paths })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -734,13 +582,7 @@ async function join(...paths: string[]): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function dirname(path: string): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'dirname',
|
||||
path
|
||||
}
|
||||
})
|
||||
return invoke('plugin:path|dirname', { path })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -756,13 +598,7 @@ async function dirname(path: string): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function extname(path: string): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'extname',
|
||||
path
|
||||
}
|
||||
})
|
||||
return invoke('plugin:path|extname', { path })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -780,14 +616,7 @@ async function extname(path: string): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function basename(path: string, ext?: string): Promise<string> {
|
||||
return invokeTauriCommand<string>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'basename',
|
||||
path,
|
||||
ext
|
||||
}
|
||||
})
|
||||
return invoke('plugin:path|basename', { path, ext })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -801,17 +630,11 @@ async function basename(path: string, ext?: string): Promise<string> {
|
||||
* @since 1.0.0
|
||||
*/
|
||||
async function isAbsolute(path: string): Promise<boolean> {
|
||||
return invokeTauriCommand<boolean>({
|
||||
__tauriModule: 'Path',
|
||||
message: {
|
||||
cmd: 'isAbsolute',
|
||||
path
|
||||
}
|
||||
})
|
||||
return invoke('plugin:path|isAbsolute', { path })
|
||||
}
|
||||
|
||||
export {
|
||||
appDir,
|
||||
BaseDirectory,
|
||||
appConfigDir,
|
||||
appDataDir,
|
||||
appLocalDataDir,
|
||||
@ -835,8 +658,6 @@ export {
|
||||
runtimeDir,
|
||||
templateDir,
|
||||
videoDir,
|
||||
logDir,
|
||||
BaseDirectory,
|
||||
sep,
|
||||
delimiter,
|
||||
resolve,
|
||||
|
Loading…
Reference in New Issue
Block a user