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 #![allow(clippy::string_slice)] use super::*;
97 use crate::CfgPath;
98
99 fn cfg_variables() -> impl IntoIterator<Item = (&'static str, PathBuf)> {
100 let list = [
101 ("ARTI_CACHE", project_dirs().unwrap().cache_dir()),
102 ("ARTI_CONFIG", project_dirs().unwrap().config_dir()),
103 ("ARTI_SHARED_DATA", project_dirs().unwrap().data_dir()),
104 ("ARTI_LOCAL_DATA", project_dirs().unwrap().data_local_dir()),
105 ("PROGRAM_DIR", &get_program_dir().unwrap()),
106 ("USER_HOME", crate::home().unwrap()),
107 ];
108
109 list.into_iter()
110 .map(|(a, b)| (a, b.to_owned()))
111 .collect::<Vec<_>>()
112 }
113
114 #[cfg(not(target_family = "windows"))]
115 #[test]
116 fn expand_variables() {
117 let path_resolver = arti_client_base_resolver();
118
119 for (var, val) in cfg_variables() {
120 let p = CfgPath::new(format!("${{{var}}}/example"));
121 assert_eq!(p.to_string(), format!("${{{var}}}/example"));
122
123 let expected = val.join("example");
124 assert_eq!(p.path(&path_resolver).unwrap().to_str(), expected.to_str());
125 }
126
127 let p = CfgPath::new("${NOT_A_REAL_VAR}/example".to_string());
128 assert!(p.path(&path_resolver).is_err());
129 }
130
131 #[cfg(target_family = "windows")]
132 #[test]
133 fn expand_variables() {
134 let path_resolver = arti_client_base_resolver();
135
136 for (var, val) in cfg_variables() {
137 let p = CfgPath::new(format!("${{{var}}}\\example"));
138 assert_eq!(p.to_string(), format!("${{{var}}}\\example"));
139
140 let expected = val.join("example");
141 assert_eq!(p.path(&path_resolver).unwrap().to_str(), expected.to_str());
142 }
143
144 let p = CfgPath::new("${NOT_A_REAL_VAR}\\example".to_string());
145 assert!(p.path(&path_resolver).is_err());
146 }
147}