tor_config_path/
arti_client_paths.rs1use std::{borrow::Cow, path::PathBuf};
7
8use directories::ProjectDirs;
9use std::sync::LazyLock;
10
11use crate::{CfgPathError, CfgPathResolver};
12
13pub fn arti_client_base_resolver() -> CfgPathResolver {
44 let arti_cache = project_dirs().map(|x| Cow::Owned(x.cache_dir().to_owned()));
45 let arti_config = project_dirs().map(|x| Cow::Owned(x.config_dir().to_owned()));
46 let arti_shared_data = project_dirs().map(|x| Cow::Owned(x.data_dir().to_owned()));
47 let arti_local_data = project_dirs().map(|x| Cow::Owned(x.data_local_dir().to_owned()));
48 let program_dir = get_program_dir().map(Cow::Owned);
49 let user_home = crate::home().map(Cow::Borrowed);
50
51 let mut resolver = CfgPathResolver::default();
52
53 resolver.set_var("ARTI_CACHE", arti_cache);
54 resolver.set_var("ARTI_CONFIG", arti_config);
55 resolver.set_var("ARTI_SHARED_DATA", arti_shared_data);
56 resolver.set_var("ARTI_LOCAL_DATA", arti_local_data);
57 resolver.set_var("PROGRAM_DIR", program_dir);
58 resolver.set_var("USER_HOME", user_home);
59
60 resolver
61}
62
63fn get_program_dir() -> Result<PathBuf, CfgPathError> {
65 let binary = std::env::current_exe().map_err(|_| CfgPathError::NoProgramPath)?;
66 let directory = binary.parent().ok_or(CfgPathError::NoProgramDir)?;
67 Ok(directory.to_owned())
68}
69
70fn project_dirs() -> Result<&'static ProjectDirs, CfgPathError> {
72 static PROJECT_DIRS: LazyLock<Option<ProjectDirs>> =
74 LazyLock::new(|| ProjectDirs::from("org", "torproject", "Arti"));
75
76 PROJECT_DIRS.as_ref().ok_or(CfgPathError::NoProjectDirs)
77}
78
79#[cfg(test)]
80mod test {
81 #![allow(clippy::bool_assert_comparison)]
83 #![allow(clippy::clone_on_copy)]
84 #![allow(clippy::dbg_macro)]
85 #![allow(clippy::mixed_attributes_style)]
86 #![allow(clippy::print_stderr)]
87 #![allow(clippy::print_stdout)]
88 #![allow(clippy::single_char_pattern)]
89 #![allow(clippy::unwrap_used)]
90 #![allow(clippy::unchecked_time_subtraction)]
91 #![allow(clippy::useless_vec)]
92 #![allow(clippy::needless_pass_by_value)]
93 use super::*;
96 use crate::CfgPath;
97
98 fn cfg_variables() -> impl IntoIterator<Item = (&'static str, PathBuf)> {
99 let list = [
100 ("ARTI_CACHE", project_dirs().unwrap().cache_dir()),
101 ("ARTI_CONFIG", project_dirs().unwrap().config_dir()),
102 ("ARTI_SHARED_DATA", project_dirs().unwrap().data_dir()),
103 ("ARTI_LOCAL_DATA", project_dirs().unwrap().data_local_dir()),
104 ("PROGRAM_DIR", &get_program_dir().unwrap()),
105 ("USER_HOME", crate::home().unwrap()),
106 ];
107
108 list.into_iter()
109 .map(|(a, b)| (a, b.to_owned()))
110 .collect::<Vec<_>>()
111 }
112
113 #[cfg(not(target_family = "windows"))]
114 #[test]
115 fn expand_variables() {
116 let path_resolver = arti_client_base_resolver();
117
118 for (var, val) in cfg_variables() {
119 let p = CfgPath::new(format!("${{{var}}}/example"));
120 assert_eq!(p.to_string(), format!("${{{var}}}/example"));
121
122 let expected = val.join("example");
123 assert_eq!(p.path(&path_resolver).unwrap().to_str(), expected.to_str());
124 }
125
126 let p = CfgPath::new("${NOT_A_REAL_VAR}/example".to_string());
127 assert!(p.path(&path_resolver).is_err());
128 }
129
130 #[cfg(target_family = "windows")]
131 #[test]
132 fn expand_variables() {
133 let path_resolver = arti_client_base_resolver();
134
135 for (var, val) in cfg_variables() {
136 let p = CfgPath::new(format!("${{{var}}}\\example"));
137 assert_eq!(p.to_string(), format!("${{{var}}}\\example"));
138
139 let expected = val.join("example");
140 assert_eq!(p.path(&path_resolver).unwrap().to_str(), expected.to_str());
141 }
142
143 let p = CfgPath::new("${NOT_A_REAL_VAR}\\example".to_string());
144 assert!(p.path(&path_resolver).is_err());
145 }
146}