use etcetera::BaseStrategy; use std::path::PathBuf; use crate::path_ext::PathExt; fn xdg_dir(env: &str) -> Option { if cfg!(windows) { let env_var = std::env::var(env).ok()?; Some(PathBuf::from(env_var)) } else { // On non-Windows platforms, `etcetera` already handles XDG variables None } } fn runtime_dir(basedirs: &impl BaseStrategy) -> Option { xdg_dir("XDG_RUNTIME_DIR").or_else(|| basedirs.runtime_dir()) } fn state_dir(basedirs: &impl BaseStrategy) -> Option { xdg_dir("XDG_STATE_HOME").or_else(|| basedirs.state_dir()) } fn cache_dir(basedirs: &impl BaseStrategy) -> PathBuf { xdg_dir("XDG_CACHE_HOME").unwrap_or_else(|| basedirs.cache_dir()) } /// A helper struct for directories in fnm that uses XDG Base Directory Specification /// if applicable for the platform. #[derive(Debug)] pub struct Directories( #[cfg(windows)] etcetera::base_strategy::Windows, #[cfg(not(windows))] etcetera::base_strategy::Xdg, ); impl Default for Directories { fn default() -> Self { Self(etcetera::choose_base_strategy().expect("choosing base strategy")) } } impl Directories { pub fn strategy(&self) -> &impl BaseStrategy { &self.0 } pub fn default_base_dir(&self) -> PathBuf { let strategy = self.strategy(); let modern = strategy.data_dir().join("fnm"); if modern.exists() { return modern; } let legacy = strategy.home_dir().join(".fnm"); if legacy.exists() { return legacy; } #[cfg(target_os = "macos")] { let basedirs = etcetera::base_strategy::Apple::new().expect("Can't get home directory"); let legacy = basedirs.data_dir().join("fnm"); if legacy.exists() { return legacy; } } modern.ensure_exists_silently() } pub fn multishell_storage(&self) -> PathBuf { let basedirs = self.strategy(); let dir = runtime_dir(basedirs) .or_else(|| state_dir(basedirs)) .unwrap_or_else(|| cache_dir(basedirs)); dir.join("fnm_multishells") } }