use std::{borrow::Cow, path::PathBuf};
use directories::ProjectDirs;
use std::sync::LazyLock;
use crate::{CfgPathError, CfgPathResolver};
pub fn arti_client_base_resolver() -> CfgPathResolver {
let arti_cache = project_dirs().map(|x| Cow::Owned(x.cache_dir().to_owned()));
let arti_config = project_dirs().map(|x| Cow::Owned(x.config_dir().to_owned()));
let arti_shared_data = project_dirs().map(|x| Cow::Owned(x.data_dir().to_owned()));
let arti_local_data = project_dirs().map(|x| Cow::Owned(x.data_local_dir().to_owned()));
let program_dir = get_program_dir().map(Cow::Owned);
let user_home = crate::home().map(Cow::Borrowed);
let mut resolver = CfgPathResolver::default();
resolver.set_var("ARTI_CACHE", arti_cache);
resolver.set_var("ARTI_CONFIG", arti_config);
resolver.set_var("ARTI_SHARED_DATA", arti_shared_data);
resolver.set_var("ARTI_LOCAL_DATA", arti_local_data);
resolver.set_var("PROGRAM_DIR", program_dir);
resolver.set_var("USER_HOME", user_home);
resolver
}
fn get_program_dir() -> Result<PathBuf, CfgPathError> {
let binary = std::env::current_exe().map_err(|_| CfgPathError::NoProgramPath)?;
let directory = binary.parent().ok_or(CfgPathError::NoProgramDir)?;
Ok(directory.to_owned())
}
fn project_dirs() -> Result<&'static ProjectDirs, CfgPathError> {
static PROJECT_DIRS: LazyLock<Option<ProjectDirs>> =
LazyLock::new(|| ProjectDirs::from("org", "torproject", "Arti"));
PROJECT_DIRS.as_ref().ok_or(CfgPathError::NoProjectDirs)
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
use crate::CfgPath;
fn cfg_variables() -> impl IntoIterator<Item = (&'static str, PathBuf)> {
let list = [
("ARTI_CACHE", project_dirs().unwrap().cache_dir()),
("ARTI_CONFIG", project_dirs().unwrap().config_dir()),
("ARTI_SHARED_DATA", project_dirs().unwrap().data_dir()),
("ARTI_LOCAL_DATA", project_dirs().unwrap().data_local_dir()),
("PROGRAM_DIR", &get_program_dir().unwrap()),
("USER_HOME", crate::home().unwrap()),
];
list.into_iter()
.map(|(a, b)| (a, b.to_owned()))
.collect::<Vec<_>>()
}
#[cfg(not(target_family = "windows"))]
#[test]
fn expand_variables() {
let path_resolver = arti_client_base_resolver();
for (var, val) in cfg_variables() {
let p = CfgPath::new(format!("${{{var}}}/example"));
assert_eq!(p.to_string(), format!("${{{var}}}/example"));
let expected = val.join("example");
assert_eq!(p.path(&path_resolver).unwrap().to_str(), expected.to_str());
}
let p = CfgPath::new("${NOT_A_REAL_VAR}/example".to_string());
assert!(p.path(&path_resolver).is_err());
}
#[cfg(target_family = "windows")]
#[test]
fn expand_variables() {
let path_resolver = arti_client_base_resolver();
for (var, val) in cfg_variables() {
let p = CfgPath::new(format!("${{{var}}}\\example"));
assert_eq!(p.to_string(), format!("${{{var}}}\\example"));
let expected = val.join("example");
assert_eq!(p.path(&path_resolver).unwrap().to_str(), expected.to_str());
}
let p = CfgPath::new("${NOT_A_REAL_VAR}\\example".to_string());
assert!(p.path(&path_resolver).is_err());
}
}