Skip to main content

browser_control/
paths.rs

1//! OS app-data directory resolution.
2
3use anyhow::{anyhow, Context, Result};
4use std::path::PathBuf;
5
6const ENV_OVERRIDE: &str = "BROWSER_CONTROL_DATA_DIR";
7
8/// Root data directory for browser-control. Used for the registry DB and profile dirs.
9///
10/// macOS:   `~/Library/Application Support/browser-control`
11/// Linux:   `$XDG_DATA_HOME/browser-control` (or `~/.local/share/browser-control`)
12/// Windows: `%APPDATA%/browser-control`
13///
14/// Override with env var `BROWSER_CONTROL_DATA_DIR` (absolute path).
15pub fn data_dir() -> Result<PathBuf> {
16    if let Some(v) = std::env::var_os(ENV_OVERRIDE) {
17        let p = PathBuf::from(v);
18        if p.as_os_str().is_empty() {
19            return Err(anyhow!("{} is set but empty", ENV_OVERRIDE));
20        }
21        return Ok(p);
22    }
23
24    let pd = directories::ProjectDirs::from("", "", "browser-control")
25        .ok_or_else(|| anyhow!("could not determine user data directory (no home dir found)"))?;
26    Ok(pd.data_dir().to_path_buf())
27}
28
29/// Returns `<data_dir>/registry.db`. Ensures the parent directory exists.
30pub fn registry_db_path() -> Result<PathBuf> {
31    let dir = data_dir()?;
32    std::fs::create_dir_all(&dir)
33        .with_context(|| format!("creating data dir {}", dir.display()))?;
34    Ok(dir.join("registry.db"))
35}
36
37/// Returns `<data_dir>/profiles`. Ensures the directory exists.
38pub fn profiles_dir() -> Result<PathBuf> {
39    let dir = data_dir()?.join("profiles");
40    std::fs::create_dir_all(&dir)
41        .with_context(|| format!("creating profiles dir {}", dir.display()))?;
42    Ok(dir)
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn paths_work() {
51        // Test 1: override via env var.
52        let tmp = tempfile::TempDir::new().unwrap();
53        let tmp_path = tmp.path().to_path_buf();
54        // Safety: tests in this module share process env; we run them sequentially
55        // within a single #[test] to avoid races with other tests.
56        std::env::set_var(ENV_OVERRIDE, &tmp_path);
57
58        assert_eq!(data_dir().unwrap(), tmp_path);
59
60        let db = registry_db_path().unwrap();
61        assert_eq!(db, tmp_path.join("registry.db"));
62        assert!(db.parent().unwrap().exists());
63        assert_eq!(db.file_name().unwrap(), "registry.db");
64
65        let pdir = profiles_dir().unwrap();
66        assert_eq!(pdir, tmp_path.join("profiles"));
67        assert!(pdir.is_dir());
68        assert_eq!(pdir.file_name().unwrap(), "profiles");
69
70        // Test 2: default path has the expected suffix.
71        std::env::remove_var(ENV_OVERRIDE);
72        let d = data_dir().unwrap();
73        assert!(
74            d.ends_with("browser-control"),
75            "expected default data_dir to end with 'browser-control', got {}",
76            d.display()
77        );
78    }
79}