1use anyhow::{anyhow, Context, Result};
4use std::path::PathBuf;
5
6use crate::detect::Kind;
7
8const ENV_OVERRIDE: &str = "BROWSER_CONTROL_DATA_DIR";
9const CONFIG_ENV_OVERRIDE: &str = "BROWSER_CONTROL_CONFIG_DIR";
10
11pub fn data_dir() -> Result<PathBuf> {
19 if let Some(v) = std::env::var_os(ENV_OVERRIDE) {
20 let p = PathBuf::from(v);
21 if p.as_os_str().is_empty() {
22 return Err(anyhow!("{} is set but empty", ENV_OVERRIDE));
23 }
24 return Ok(p);
25 }
26
27 let pd = directories::ProjectDirs::from("", "", "browser-control")
28 .ok_or_else(|| anyhow!("could not determine user data directory (no home dir found)"))?;
29 Ok(pd.data_dir().to_path_buf())
30}
31
32pub fn registry_db_path() -> Result<PathBuf> {
34 let dir = data_dir()?;
35 std::fs::create_dir_all(&dir)
36 .with_context(|| format!("creating data dir {}", dir.display()))?;
37 Ok(dir.join("registry.db"))
38}
39
40pub fn config_dir() -> Result<PathBuf> {
48 if let Some(v) = std::env::var_os(CONFIG_ENV_OVERRIDE) {
49 let p = PathBuf::from(v);
50 if p.as_os_str().is_empty() {
51 return Err(anyhow!("{} is set but empty", CONFIG_ENV_OVERRIDE));
52 }
53 return Ok(p);
54 }
55
56 let pd = directories::ProjectDirs::from("", "", "browser-control")
57 .ok_or_else(|| anyhow!("could not determine user config directory (no home dir found)"))?;
58 Ok(pd.config_dir().to_path_buf())
59}
60
61pub fn config_file_path() -> Result<PathBuf> {
63 let dir = config_dir()?;
64 std::fs::create_dir_all(&dir)
65 .with_context(|| format!("creating config dir {}", dir.display()))?;
66 Ok(dir.join("config.toml"))
67}
68
69pub fn profiles_dir() -> Result<PathBuf> {
71 let dir = data_dir()?.join("profiles");
72 std::fs::create_dir_all(&dir)
73 .with_context(|| format!("creating profiles dir {}", dir.display()))?;
74 Ok(dir)
75}
76
77pub fn default_profile_dir(kind: Kind) -> Result<PathBuf> {
93 let dir = config_dir()?
94 .join("profiles")
95 .join(kind.as_str())
96 .join("default");
97 std::fs::create_dir_all(&dir)
98 .with_context(|| format!("creating default profile dir {}", dir.display()))?;
99 Ok(dir)
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn paths_work() {
108 let _g = crate::test_support::ENV_LOCK
109 .lock()
110 .unwrap_or_else(|e| e.into_inner());
111 let tmp = tempfile::TempDir::new().unwrap();
113 let tmp_path = tmp.path().to_path_buf();
114 std::env::set_var(ENV_OVERRIDE, &tmp_path);
117
118 assert_eq!(data_dir().unwrap(), tmp_path);
119
120 let db = registry_db_path().unwrap();
121 assert_eq!(db, tmp_path.join("registry.db"));
122 assert!(db.parent().unwrap().exists());
123 assert_eq!(db.file_name().unwrap(), "registry.db");
124
125 let pdir = profiles_dir().unwrap();
126 assert_eq!(pdir, tmp_path.join("profiles"));
127 assert!(pdir.is_dir());
128 assert_eq!(pdir.file_name().unwrap(), "profiles");
129
130 std::env::remove_var(ENV_OVERRIDE);
132 let d = data_dir().unwrap();
133 assert!(
134 d.ends_with("browser-control"),
135 "expected default data_dir to end with 'browser-control', got {}",
136 d.display()
137 );
138 }
139
140 #[test]
141 fn config_paths_work() {
142 let _g = crate::test_support::ENV_LOCK
143 .lock()
144 .unwrap_or_else(|e| e.into_inner());
145 let tmp = tempfile::TempDir::new().unwrap();
146 let tmp_path = tmp.path().to_path_buf();
147 std::env::set_var(CONFIG_ENV_OVERRIDE, &tmp_path);
148
149 assert_eq!(config_dir().unwrap(), tmp_path);
150
151 let cfg = config_file_path().unwrap();
152 assert_eq!(cfg, tmp_path.join("config.toml"));
153 assert!(cfg.parent().unwrap().exists());
154 assert_eq!(cfg.file_name().unwrap(), "config.toml");
155
156 std::env::remove_var(CONFIG_ENV_OVERRIDE);
157 let d = config_dir().unwrap();
158 assert!(
159 d.ends_with("browser-control"),
160 "expected default config_dir to end with 'browser-control', got {}",
161 d.display()
162 );
163 }
164
165 #[test]
166 fn default_profile_dir_honours_config_env_override() {
167 let _g = crate::test_support::ENV_LOCK
168 .lock()
169 .unwrap_or_else(|e| e.into_inner());
170 let tmp = tempfile::TempDir::new().unwrap();
171 let tmp_path = tmp.path().to_path_buf();
172 std::env::set_var(CONFIG_ENV_OVERRIDE, &tmp_path);
173
174 for k in [
175 Kind::Chrome,
176 Kind::Edge,
177 Kind::Chromium,
178 Kind::Brave,
179 Kind::Firefox,
180 ] {
181 let p = default_profile_dir(k).unwrap();
182 assert_eq!(
183 p,
184 tmp_path.join("profiles").join(k.as_str()).join("default")
185 );
186 assert!(p.is_dir(), "expected {} to be created", p.display());
187 }
188
189 std::env::remove_var(CONFIG_ENV_OVERRIDE);
190 }
191}