use std::path::{Component, Path, PathBuf};
use crate::{Config, Env, PackageInfo};
use serde_repr::{Deserialize_repr, Serialize_repr};
#[derive(Serialize_repr, Deserialize_repr, Clone, Copy, Debug)]
#[repr(u16)]
#[non_exhaustive]
pub enum BaseDirectory {
Audio = 1,
Cache,
Config,
Data,
LocalData,
Desktop,
Document,
Download,
Executable,
Font,
Home,
Picture,
Public,
Runtime,
Template,
Video,
Resource,
App,
Log,
}
impl BaseDirectory {
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",
Self::App => "$APP",
Self::Log => "$LOG",
}
}
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,
"$APP" => Self::App,
"$LOG" => Self::Log,
_ => return None,
};
Some(res)
}
}
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();
if let Some(Component::Normal(str)) = components.next() {
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);
}
}
for component in components {
if let Component::ParentDir = component {
continue;
}
p.push(component);
}
Ok(p)
}
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),
BaseDirectory::App => app_dir(config),
BaseDirectory::Log => log_dir(config),
};
if let Some(mut base_dir_path_value) = base_dir_path {
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)
}
}
pub fn audio_dir() -> Option<PathBuf> {
dirs_next::audio_dir()
}
pub fn cache_dir() -> Option<PathBuf> {
dirs_next::cache_dir()
}
pub fn config_dir() -> Option<PathBuf> {
dirs_next::config_dir()
}
pub fn data_dir() -> Option<PathBuf> {
dirs_next::data_dir()
}
pub fn local_data_dir() -> Option<PathBuf> {
dirs_next::data_local_dir()
}
pub fn desktop_dir() -> Option<PathBuf> {
dirs_next::desktop_dir()
}
pub fn document_dir() -> Option<PathBuf> {
dirs_next::document_dir()
}
pub fn download_dir() -> Option<PathBuf> {
dirs_next::download_dir()
}
pub fn executable_dir() -> Option<PathBuf> {
dirs_next::executable_dir()
}
pub fn font_dir() -> Option<PathBuf> {
dirs_next::font_dir()
}
pub fn home_dir() -> Option<PathBuf> {
dirs_next::home_dir()
}
pub fn picture_dir() -> Option<PathBuf> {
dirs_next::picture_dir()
}
pub fn public_dir() -> Option<PathBuf> {
dirs_next::public_dir()
}
pub fn runtime_dir() -> Option<PathBuf> {
dirs_next::runtime_dir()
}
pub fn template_dir() -> Option<PathBuf> {
dirs_next::template_dir()
}
pub fn video_dir() -> Option<PathBuf> {
dirs_next::video_dir()
}
pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> Option<PathBuf> {
crate::utils::platform::resource_dir(package_info, env).ok()
}
pub fn app_dir(config: &Config) -> Option<PathBuf> {
dirs_next::config_dir().map(|dir| dir.join(&config.tauri.bundle.identifier))
}
pub fn 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
}