#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Dir {
pub prefix: DirPrefix,
pub salt: String,
pub path: String,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CacheDir {
pub prefix: DirPrefix,
pub path: String,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Include {
pub prefix: DirPrefix,
pub ignore_missing: bool,
pub path: String,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RemapDir {
pub prefix: DirPrefix,
pub as_path: String,
pub salt: String,
pub path: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DirPrefix {
Default,
Cwd,
Xdg,
Relative,
}
pub enum PrefixBehavior {
Config,
Cwd,
Xdg,
Relative,
}
parse_enum! {
DirPrefix,
(Default, "default"),
(Cwd, "cwd"),
(Xdg, "xdg"),
(Relative, "relative"),
}
impl Default for DirPrefix {
fn default() -> Self {
DirPrefix::Default
}
}
#[allow(unused_mut, clippy::let_and_return)]
fn config_home() -> Result<String, std::env::VarError> {
let mut home = std::env::var("HOME");
#[cfg(target_os = "windows")]
{
home = home.or_else(|_| std::env::var("USERPROFILE"));
}
home
}
fn config_get_file_name(p: &std::path::PathBuf) -> std::path::PathBuf {
if cfg!(target_os = "windows") {
return p.clone();
} else {
std::path::Path::new("/etc/fonts").join(p)
}
}
fn expand_tilde(path: &String) -> std::path::PathBuf {
let parsed_path = std::path::Path::new(path);
if let Ok(stripped_path) = parsed_path.strip_prefix("~") {
let home = config_home().unwrap_or("/".to_string());
std::path::Path::new(&home).join(stripped_path)
} else {
parsed_path.into()
}
}
macro_rules! define_calculate_path {
($ty:ident, $xdg_env:expr, $xdg_fallback:expr, $default_prefix_behavior:expr) => {
impl $ty {
pub const XDG_ENV: &'static str = $xdg_env;
pub const XDG_FALLBACK_PATH: &'static str = $xdg_fallback;
const DEFAULT_PREFIX_BEHAVIOR: PrefixBehavior = $default_prefix_behavior;
fn get_prefix_behavior(prefix: DirPrefix) -> PrefixBehavior {
match prefix {
DirPrefix::Default => Self::DEFAULT_PREFIX_BEHAVIOR,
DirPrefix::Cwd => PrefixBehavior::Cwd,
DirPrefix::Xdg => PrefixBehavior::Xdg,
DirPrefix::Relative => PrefixBehavior::Relative,
}
}
pub fn calculate_path<P: AsRef<std::path::Path> + ?Sized>(
&self,
config_file_path: &P,
) -> std::path::PathBuf {
let expanded_path = expand_tilde(&self.path);
if expanded_path.is_absolute() {
return expanded_path;
}
let prefix = Self::get_prefix_behavior(self.prefix);
match prefix {
PrefixBehavior::Config => config_get_file_name(&expanded_path),
PrefixBehavior::Cwd => std::path::Path::new(".").join(expanded_path),
PrefixBehavior::Relative => match config_file_path.as_ref().parent() {
Some(parent) => parent.join(expanded_path),
None => std::path::Path::new(".").join(expanded_path),
},
PrefixBehavior::Xdg => {
let xdg_path =
std::env::var($xdg_env).unwrap_or_else(|_| $xdg_fallback.into());
expand_tilde(&xdg_path).join(expanded_path)
}
}
}
}
};
}
define_calculate_path!(Dir, "XDG_DATA_HOME", "~/.local/share", PrefixBehavior::Cwd);
define_calculate_path!(CacheDir, "XDG_CACHE_HOME", "~/.cache", PrefixBehavior::Cwd);
define_calculate_path!(
Include,
"XDG_CONFIG_HOME",
"~/.config",
PrefixBehavior::Config
);
define_calculate_path!(
RemapDir,
"XDG_CONFIG_HOME",
"~/.config",
PrefixBehavior::Cwd
);