refactor(core): use bundle identifier on app dir, closes #1693 (#1703)

* refactor(core): use bundle identifier on app dir, closes #1693

* fix: tests

* clippy
This commit is contained in:
Lucas Fernandes Nogueira 2021-05-04 20:26:13 -03:00 committed by GitHub
parent 935db26c01
commit 428d50add4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 93 deletions

View File

@ -0,0 +1,6 @@
---
"tauri": patch
---
**Breaking:** `api::path::resolve_path()` and `api::path::app_dir()` now takes the config as first argument.
**Breaking:** `api::path::app_dir()` now resolves to `${configDir}/${config.tauri.bundle.identifier}`.

View File

@ -397,7 +397,7 @@ pub struct PackageConfig {
}
/// The tauri.conf.json mapper.
#[derive(Debug, PartialEq, Deserialize)]
#[derive(Debug, Default, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
/// Package settings.

View File

@ -7,6 +7,8 @@ use std::{
path::{Path, PathBuf},
};
use crate::Config;
use serde_repr::{Deserialize_repr, Serialize_repr};
/// A Base Directory to use.
@ -52,7 +54,7 @@ pub enum BaseDirectory {
/// The Resource directory.
Resource,
/// The default App config directory.
/// Resolves to ${CONFIG_DIR}/${APP_NAME}
/// Resolves to ${BaseDirectory::Config}/${config.tauri.bundle.identifier}
App,
/// The current working directory.
Current,
@ -63,11 +65,13 @@ pub enum BaseDirectory {
/// # Example
/// ```
/// use tauri::api::path::{resolve_path, BaseDirectory};
/// let path = resolve_path("path/to/something", Some(BaseDirectory::Config))
/// // we use the default config, but in an actual app you should get the Config created from tauri.conf.json
/// let path = resolve_path(&Default::default(), "path/to/something", Some(BaseDirectory::Config))
/// .expect("failed to resolve path");
/// // path is equal to "/home/${whoami}/.config/path/to/something" on Linux
/// ```
pub fn resolve_path<P: AsRef<Path>>(
config: &Config,
path: P,
dir: Option<BaseDirectory>,
) -> crate::api::Result<PathBuf> {
@ -90,7 +94,7 @@ pub fn resolve_path<P: AsRef<Path>>(
BaseDirectory::Template => template_dir(),
BaseDirectory::Video => video_dir(),
BaseDirectory::Resource => resource_dir(),
BaseDirectory::App => app_dir(),
BaseDirectory::App => app_dir(config),
BaseDirectory::Current => Some(env::current_dir()?),
};
if let Some(mut base_dir_path_value) = base_dir_path {
@ -193,24 +197,7 @@ pub fn resource_dir() -> Option<PathBuf> {
crate::api::platform::resource_dir().ok()
}
fn app_name() -> crate::api::Result<String> {
let exe = std::env::current_exe()?;
let app_name = exe
.file_stem()
.expect("failed to get exe filename")
.to_string_lossy();
Ok(app_name.to_string())
}
/// Returns the path to the suggested directory for your app config files.
pub fn app_dir() -> Option<PathBuf> {
dirs_next::config_dir().and_then(|mut dir| {
if let Ok(app_name) = app_name() {
dir.push(app_name);
Some(dir)
} else {
None
}
})
pub fn app_dir(config: &Config) -> Option<PathBuf> {
dirs_next::config_dir().map(|dir| dir.join(&config.tauri.bundle.identifier))
}

View File

@ -9,6 +9,9 @@ use crate::{
};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::sync::Arc;
mod app;
mod cli;
mod dialog;
@ -58,7 +61,7 @@ impl Module {
self,
window: Window<M>,
resolver: InvokeResolver<M>,
config: &Config,
config: Arc<Config>,
package_info: PackageInfo,
) {
match self {
@ -70,8 +73,12 @@ impl Module {
}),
Self::Process(cmd) => resolver
.respond_async(async move { cmd.run().and_then(|r| r.json).map_err(InvokeError::from) }),
Self::Fs(cmd) => resolver
.respond_async(async move { cmd.run().and_then(|r| r.json).map_err(InvokeError::from) }),
Self::Fs(cmd) => resolver.respond_async(async move {
cmd
.run(config)
.and_then(|r| r.json)
.map_err(InvokeError::from)
}),
Self::Window(cmd) => resolver.respond_async(async move {
cmd
.run(window)
@ -113,15 +120,12 @@ impl Module {
})
}
}
Self::Notification(cmd) => {
let identifier = config.tauri.bundle.identifier.clone();
resolver.respond_async(async move {
cmd
.run(identifier)
.and_then(|r| r.json)
.map_err(InvokeError::from)
})
}
Self::Notification(cmd) => resolver.respond_closure(move || {
cmd
.run(config)
.and_then(|r| r.json)
.map_err(InvokeError::from)
}),
Self::Http(cmd) => resolver.respond_async(async move {
cmd
.run()
@ -142,7 +146,7 @@ impl Module {
pub(crate) fn handle<P: Params>(
module: String,
invoke: Invoke<P>,
config: &Config,
config: Arc<Config>,
package_info: &PackageInfo,
) {
let Invoke { message, resolver } = invoke;

View File

@ -3,12 +3,17 @@
// SPDX-License-Identifier: MIT
use super::InvokeResponse;
use crate::api::path::BaseDirectory;
use crate::{
api::{
dir, file,
path::{resolve_path, BaseDirectory},
},
Config,
};
use crate::api::{dir, file, path::resolve_path};
use serde::{Deserialize, Serialize};
use std::{fs, fs::File, io::Write, path::PathBuf};
use std::{fs, fs::File, io::Write, path::PathBuf, sync::Arc};
/// The options for the directory functions on the file system API.
#[derive(Deserialize)]
@ -96,17 +101,22 @@ pub enum Cmd {
}
impl Cmd {
pub fn run(self) -> crate::Result<InvokeResponse> {
#[allow(unused_variables)]
pub fn run(self, config: Arc<Config>) -> crate::Result<InvokeResponse> {
match self {
#[cfg(fs_read_text_file)]
Self::ReadTextFile { path, options } => read_text_file(path, options).map(Into::into),
Self::ReadTextFile { path, options } => {
read_text_file(&config, path, options).map(Into::into)
}
#[cfg(not(fs_read_text_file))]
Self::ReadTextFile { .. } => Err(crate::Error::ApiNotAllowlisted(
"fs > readTextFile".to_string(),
)),
#[cfg(fs_read_binary_file)]
Self::ReadBinaryFile { path, options } => read_binary_file(path, options).map(Into::into),
Self::ReadBinaryFile { path, options } => {
read_binary_file(&config, path, options).map(Into::into)
}
#[cfg(not(fs_read_binary_file))]
Self::ReadBinaryFile { .. } => Err(crate::Error::ApiNotAllowlisted(
"readBinaryFile".to_string(),
@ -117,7 +127,7 @@ impl Cmd {
path,
contents,
options,
} => write_file(path, contents, options).map(Into::into),
} => write_file(&config, path, contents, options).map(Into::into),
#[cfg(not(fs_write_file))]
Self::WriteFile { .. } => Err(crate::Error::ApiNotAllowlisted(
"fs > writeFile".to_string(),
@ -128,14 +138,14 @@ impl Cmd {
path,
contents,
options,
} => write_binary_file(path, contents, options).map(Into::into),
} => write_binary_file(&config, path, contents, options).map(Into::into),
#[cfg(not(fs_write_binary_file))]
Self::WriteBinaryFile { .. } => Err(crate::Error::ApiNotAllowlisted(
"writeBinaryFile".to_string(),
)),
#[cfg(fs_read_dir)]
Self::ReadDir { path, options } => read_dir(path, options).map(Into::into),
Self::ReadDir { path, options } => read_dir(&config, path, options).map(Into::into),
#[cfg(not(fs_read_dir))]
Self::ReadDir { .. } => Err(crate::Error::ApiNotAllowlisted("fs > readDir".to_string())),
@ -144,26 +154,26 @@ impl Cmd {
source,
destination,
options,
} => copy_file(source, destination, options).map(Into::into),
} => copy_file(&config, source, destination, options).map(Into::into),
#[cfg(not(fs_copy_file))]
Self::CopyFile { .. } => Err(crate::Error::ApiNotAllowlisted("fs > copyFile".to_string())),
#[cfg(fs_create_dir)]
Self::CreateDir { path, options } => create_dir(path, options).map(Into::into),
Self::CreateDir { path, options } => create_dir(&config, path, options).map(Into::into),
#[cfg(not(fs_create_dir))]
Self::CreateDir { .. } => Err(crate::Error::ApiNotAllowlisted(
"fs > createDir".to_string(),
)),
#[cfg(fs_remove_dir)]
Self::RemoveDir { path, options } => remove_dir(path, options).map(Into::into),
Self::RemoveDir { path, options } => remove_dir(&config, path, options).map(Into::into),
#[cfg(not(fs_remove_dir))]
Self::RemoveDir { .. } => Err(crate::Error::ApiNotAllowlisted(
"fs > removeDir".to_string(),
)),
#[cfg(fs_remove_file)]
Self::RemoveFile { path, options } => remove_file(path, options).map(Into::into),
Self::RemoveFile { path, options } => remove_file(&config, path, options).map(Into::into),
#[cfg(not(fs_remove_file))]
Self::RemoveFile { .. } => Err(crate::Error::ApiNotAllowlisted(
"fs > removeFile".to_string(),
@ -174,7 +184,7 @@ impl Cmd {
old_path,
new_path,
options,
} => rename_file(old_path, new_path, options).map(Into::into),
} => rename_file(&config, old_path, new_path, options).map(Into::into),
#[cfg(not(fs_rename_file))]
Self::RenameFile { .. } => Err(crate::Error::ApiNotAllowlisted(
"fs > renameFile".to_string(),
@ -182,7 +192,7 @@ impl Cmd {
#[cfg(fs_path)]
Self::ResolvePath { path, directory } => {
resolve_path_handler(path, directory).map(Into::into)
resolve_path_handler(&config, path, directory).map(Into::into)
}
#[cfg(not(fs_path))]
Self::ResolvePath { .. } => Err(crate::Error::ApiNotAllowlisted("fs > pathApi".to_string())),
@ -193,6 +203,7 @@ impl Cmd {
/// Reads a directory.
#[cfg(fs_read_dir)]
pub fn read_dir(
config: &Config,
path: PathBuf,
options: Option<DirOperationOptions>,
) -> crate::Result<Vec<dir::DiskEntry>> {
@ -201,20 +212,22 @@ pub fn read_dir(
} else {
(false, None)
};
dir::read_dir(resolve_path(path, dir)?, recursive).map_err(crate::Error::FailedToExecuteApi)
dir::read_dir(resolve_path(config, path, dir)?, recursive)
.map_err(crate::Error::FailedToExecuteApi)
}
/// Copies a file.
#[cfg(fs_copy_file)]
pub fn copy_file(
config: &Config,
source: PathBuf,
destination: PathBuf,
options: Option<FileOperationOptions>,
) -> crate::Result<()> {
let (src, dest) = match options.and_then(|o| o.dir) {
Some(dir) => (
resolve_path(source, Some(dir.clone()))?,
resolve_path(destination, Some(dir))?,
resolve_path(config, source, Some(dir.clone()))?,
resolve_path(config, destination, Some(dir))?,
),
None => (source, destination),
};
@ -224,13 +237,17 @@ pub fn copy_file(
/// Creates a directory.
#[cfg(fs_create_dir)]
pub fn create_dir(path: PathBuf, options: Option<DirOperationOptions>) -> crate::Result<()> {
pub fn create_dir(
config: &Config,
path: PathBuf,
options: Option<DirOperationOptions>,
) -> crate::Result<()> {
let (recursive, dir) = if let Some(options_value) = options {
(options_value.recursive, options_value.dir)
} else {
(false, None)
};
let resolved_path = resolve_path(path, dir)?;
let resolved_path = resolve_path(config, path, dir)?;
if recursive {
fs::create_dir_all(resolved_path)?;
} else {
@ -242,13 +259,17 @@ pub fn create_dir(path: PathBuf, options: Option<DirOperationOptions>) -> crate:
/// Removes a directory.
#[cfg(fs_remove_dir)]
pub fn remove_dir(path: PathBuf, options: Option<DirOperationOptions>) -> crate::Result<()> {
pub fn remove_dir(
config: &Config,
path: PathBuf,
options: Option<DirOperationOptions>,
) -> crate::Result<()> {
let (recursive, dir) = if let Some(options_value) = options {
(options_value.recursive, options_value.dir)
} else {
(false, None)
};
let resolved_path = resolve_path(path, dir)?;
let resolved_path = resolve_path(config, path, dir)?;
if recursive {
fs::remove_dir_all(resolved_path)?;
} else {
@ -260,8 +281,12 @@ pub fn remove_dir(path: PathBuf, options: Option<DirOperationOptions>) -> crate:
/// Removes a file
#[cfg(fs_remove_file)]
pub fn remove_file(path: PathBuf, options: Option<FileOperationOptions>) -> crate::Result<()> {
let resolved_path = resolve_path(path, options.and_then(|o| o.dir))?;
pub fn remove_file(
config: &Config,
path: PathBuf,
options: Option<FileOperationOptions>,
) -> crate::Result<()> {
let resolved_path = resolve_path(config, path, options.and_then(|o| o.dir))?;
fs::remove_file(resolved_path)?;
Ok(())
}
@ -269,14 +294,15 @@ pub fn remove_file(path: PathBuf, options: Option<FileOperationOptions>) -> crat
/// Renames a file.
#[cfg(fs_rename_file)]
pub fn rename_file(
config: &Config,
old_path: PathBuf,
new_path: PathBuf,
options: Option<FileOperationOptions>,
) -> crate::Result<()> {
let (old, new) = match options.and_then(|o| o.dir) {
Some(dir) => (
resolve_path(old_path, Some(dir.clone()))?,
resolve_path(new_path, Some(dir))?,
resolve_path(config, old_path, Some(dir.clone()))?,
resolve_path(config, new_path, Some(dir))?,
),
None => (old_path, new_path),
};
@ -286,11 +312,12 @@ pub fn rename_file(
/// Writes a text file.
#[cfg(fs_write_file)]
pub fn write_file(
config: &Config,
path: PathBuf,
contents: String,
options: Option<FileOperationOptions>,
) -> crate::Result<()> {
File::create(resolve_path(path, options.and_then(|o| o.dir))?)
File::create(resolve_path(config, path, options.and_then(|o| o.dir))?)
.map_err(crate::Error::Io)
.and_then(|mut f| f.write_all(contents.as_bytes()).map_err(|err| err.into()))?;
Ok(())
@ -299,6 +326,7 @@ pub fn write_file(
/// Writes a binary file.
#[cfg(fs_write_binary_file)]
pub fn write_binary_file(
config: &Config,
path: PathBuf,
contents: String,
options: Option<FileOperationOptions>,
@ -306,7 +334,7 @@ pub fn write_binary_file(
base64::decode(contents)
.map_err(crate::Error::Base64Decode)
.and_then(|c| {
File::create(resolve_path(path, options.and_then(|o| o.dir))?)
File::create(resolve_path(config, path, options.and_then(|o| o.dir))?)
.map_err(Into::into)
.and_then(|mut f| f.write_all(&c).map_err(|err| err.into()))
})?;
@ -316,29 +344,32 @@ pub fn write_binary_file(
/// Reads a text file.
#[cfg(fs_read_text_file)]
pub fn read_text_file(
config: &Config,
path: PathBuf,
options: Option<FileOperationOptions>,
) -> crate::Result<String> {
file::read_string(resolve_path(path, options.and_then(|o| o.dir))?)
file::read_string(resolve_path(config, path, options.and_then(|o| o.dir))?)
.map_err(crate::Error::FailedToExecuteApi)
}
/// Reads a binary file.
#[cfg(fs_read_binary_file)]
pub fn read_binary_file(
config: &Config,
path: PathBuf,
options: Option<FileOperationOptions>,
) -> crate::Result<Vec<u8>> {
file::read_binary(resolve_path(path, options.and_then(|o| o.dir))?)
file::read_binary(resolve_path(config, path, options.and_then(|o| o.dir))?)
.map_err(crate::Error::FailedToExecuteApi)
}
#[cfg(fs_path)]
pub fn resolve_path_handler(
config: &Config,
path: String,
directory: Option<BaseDirectory>,
) -> crate::Result<PathBuf> {
resolve_path(path, directory).map_err(Into::into)
resolve_path(config, path, directory).map_err(Into::into)
}
// test webview functionality.

View File

@ -7,6 +7,9 @@ use serde::Deserialize;
#[cfg(notification_all)]
use crate::api::notification::Notification;
use crate::Config;
use std::sync::Arc;
// `Granted` response from `request_permission`. Matches the Web API return value.
#[cfg(notification_all)]
@ -39,21 +42,21 @@ pub enum Cmd {
impl Cmd {
#[allow(unused_variables)]
pub fn run(self, identifier: String) -> crate::Result<InvokeResponse> {
pub fn run(self, config: Arc<Config>) -> crate::Result<InvokeResponse> {
match self {
#[cfg(notification_all)]
Self::Notification { options } => send(options, identifier).map(Into::into),
Self::Notification { options } => send(options, &config).map(Into::into),
#[cfg(not(notification_all))]
Self::Notification { .. } => Err(crate::Error::ApiNotAllowlisted("notification".to_string())),
Self::IsNotificationPermissionGranted => {
#[cfg(notification_all)]
return is_permission_granted().map(Into::into);
return is_permission_granted(&config).map(Into::into);
#[cfg(not(notification_all))]
Ok(false.into())
}
Self::RequestNotificationPermission => {
#[cfg(notification_all)]
return request_permission().map(Into::into);
return request_permission(&config).map(Into::into);
#[cfg(not(notification_all))]
Ok(PERMISSION_DENIED.into())
}
@ -62,8 +65,9 @@ impl Cmd {
}
#[cfg(notification_all)]
pub fn send(options: NotificationOptions, identifier: String) -> crate::Result<InvokeResponse> {
let mut notification = Notification::new(identifier).title(options.title);
pub fn send(options: NotificationOptions, config: &Config) -> crate::Result<InvokeResponse> {
let mut notification =
Notification::new(config.tauri.bundle.identifier.clone()).title(options.title);
if let Some(body) = options.body {
notification = notification.body(body);
}
@ -75,8 +79,8 @@ pub fn send(options: NotificationOptions, identifier: String) -> crate::Result<I
}
#[cfg(notification_all)]
pub fn is_permission_granted() -> crate::Result<InvokeResponse> {
let settings = crate::settings::read_settings()?;
pub fn is_permission_granted(config: &Config) -> crate::Result<InvokeResponse> {
let settings = crate::settings::read_settings(config)?;
if let Some(allow_notification) = settings.allow_notification {
Ok(allow_notification.into())
} else {
@ -85,8 +89,8 @@ pub fn is_permission_granted() -> crate::Result<InvokeResponse> {
}
#[cfg(notification_all)]
pub fn request_permission() -> crate::Result<String> {
let mut settings = crate::settings::read_settings()?;
pub fn request_permission(config: &Config) -> crate::Result<String> {
let mut settings = crate::settings::read_settings(config)?;
if let Some(allow_notification) = settings.allow_notification {
return Ok(if allow_notification {
PERMISSION_GRANTED.to_string()
@ -101,12 +105,12 @@ pub fn request_permission() -> crate::Result<String> {
match answer {
crate::api::dialog::AskResponse::Yes => {
settings.allow_notification = Some(true);
crate::settings::write_settings(settings)?;
crate::settings::write_settings(config, settings)?;
Ok(PERMISSION_GRANTED.to_string())
}
crate::api::dialog::AskResponse::No => {
settings.allow_notification = Some(false);
crate::settings::write_settings(settings)?;
crate::settings::write_settings(config, settings)?;
Ok(PERMISSION_DENIED.to_string())
}
}

View File

@ -144,7 +144,7 @@ pub trait Params: sealed::ParamsBase {
/// TODO: expand these docs
pub trait Manager<P: Params>: sealed::ManagerBase<P> {
/// The [`Config`] the manager was created with.
fn config(&self) -> &Config {
fn config(&self) -> Arc<Config> {
self.manager().config()
}

View File

@ -62,7 +62,7 @@ pub struct InnerWindowManager<P: Params> {
/// The page load hook, invoked when the webview performs a navigation.
on_page_load: Box<OnPageLoad<P>>,
config: Config,
config: Arc<Config>,
assets: Arc<P::Assets>,
default_window_icon: Option<Vec<u8>>,
@ -133,7 +133,7 @@ impl<P: Params> WindowManager<P> {
state: Arc::new(state),
invoke_handler,
on_page_load,
config: context.config,
config: Arc::new(context.config),
assets: context.assets,
default_window_icon: context.default_window_icon,
salts: Mutex::default(),
@ -215,6 +215,7 @@ impl<P: Params> WindowManager<P> {
}
let local_app_data = resolve_path(
&self.inner.config,
&self.inner.config.tauri.bundle.identifier,
Some(BaseDirectory::LocalData),
);
@ -525,12 +526,15 @@ impl<P: Params> WindowManager<P> {
pub fn labels(&self) -> HashSet<P::Label> {
self.windows_lock().keys().cloned().collect()
}
pub fn config(&self) -> &Config {
&self.inner.config
pub fn config(&self) -> Arc<Config> {
self.inner.config.clone()
}
pub fn package_info(&self) -> &PackageInfo {
&self.inner.package_info
}
pub fn unlisten(&self, handler_id: EventHandler) {
self.inner.listeners.unlisten(handler_id)
}

View File

@ -2,9 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::api::{
file::read_string,
path::{resolve_path, BaseDirectory},
use crate::{
api::{
file::read_string,
path::{resolve_path, BaseDirectory},
},
Config,
};
use serde::{Deserialize, Serialize};
use std::{
@ -22,14 +25,14 @@ pub struct Settings {
}
/// Gets the path to the settings file
fn get_settings_path() -> crate::api::Result<PathBuf> {
resolve_path(".tauri-settings.json", Some(BaseDirectory::App))
fn get_settings_path(config: &Config) -> crate::api::Result<PathBuf> {
resolve_path(config, ".tauri-settings.json", Some(BaseDirectory::App))
}
/// Write the settings to the file system.
#[allow(dead_code)]
pub(crate) fn write_settings(settings: Settings) -> crate::Result<()> {
let settings_path = get_settings_path()?;
pub(crate) fn write_settings(config: &Config, settings: Settings) -> crate::Result<()> {
let settings_path = get_settings_path(config)?;
let settings_folder = Path::new(&settings_path).parent().unwrap();
if !settings_folder.exists() {
std::fs::create_dir(settings_folder)?;
@ -43,8 +46,8 @@ pub(crate) fn write_settings(settings: Settings) -> crate::Result<()> {
}
/// Reads the settings from the file system.
pub fn read_settings() -> crate::Result<Settings> {
let settings_path = get_settings_path()?;
pub fn read_settings(config: &Config) -> crate::Result<Settings> {
let settings_path = get_settings_path(config)?;
if settings_path.exists() {
read_string(settings_path)
.and_then(|settings| serde_json::from_str(settings.as_str()).map_err(Into::into))