Skip to main content

git_paw/
dirs.rs

1//! Platform directory helpers.
2//!
3//! Provides XDG-compatible data and config directory resolution without
4//! external dependencies. Matches the behaviour of the `dirs` crate:
5//!
6//! | Platform | Config dir                        | Data dir                          |
7//! |----------|-----------------------------------|-----------------------------------|
8//! | Linux    | `$XDG_CONFIG_HOME` or `~/.config` | `$XDG_DATA_HOME` or `~/.local/share` |
9//! | macOS    | `~/Library/Application Support`   | `~/Library/Application Support`   |
10
11use std::path::PathBuf;
12
13/// Returns the user's home directory, or `None` if it cannot be determined.
14fn home_dir() -> Option<PathBuf> {
15    std::env::var_os("HOME").map(PathBuf::from)
16}
17
18/// Returns the platform config directory.
19///
20/// - **Linux:** `$XDG_CONFIG_HOME` or `~/.config`
21/// - **macOS:** `~/Library/Application Support`
22pub fn config_dir() -> Option<PathBuf> {
23    #[cfg(target_os = "macos")]
24    {
25        home_dir().map(|h| h.join("Library/Application Support"))
26    }
27    #[cfg(not(target_os = "macos"))]
28    {
29        std::env::var_os("XDG_CONFIG_HOME")
30            .map(PathBuf::from)
31            .filter(|p| p.is_absolute())
32            .or_else(|| home_dir().map(|h| h.join(".config")))
33    }
34}
35
36/// Returns the platform data directory.
37///
38/// - **Linux:** `$XDG_DATA_HOME` or `~/.local/share`
39/// - **macOS:** `~/Library/Application Support`
40pub fn data_dir() -> Option<PathBuf> {
41    #[cfg(target_os = "macos")]
42    {
43        home_dir().map(|h| h.join("Library/Application Support"))
44    }
45    #[cfg(not(target_os = "macos"))]
46    {
47        std::env::var_os("XDG_DATA_HOME")
48            .map(PathBuf::from)
49            .filter(|p| p.is_absolute())
50            .or_else(|| home_dir().map(|h| h.join(".local/share")))
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn config_dir_returns_some_when_home_is_set() {
60        // HOME is always set in test environments
61        let dir = config_dir();
62        assert!(dir.is_some(), "config_dir should return Some");
63        assert!(dir.unwrap().is_absolute(), "config_dir should be absolute");
64    }
65
66    #[test]
67    fn data_dir_returns_some_when_home_is_set() {
68        let dir = data_dir();
69        assert!(dir.is_some(), "data_dir should return Some");
70        assert!(dir.unwrap().is_absolute(), "data_dir should be absolute");
71    }
72
73    #[test]
74    fn config_and_data_dirs_live_under_home() {
75        let home = home_dir().expect("HOME should be set in tests");
76        let config = config_dir().unwrap();
77        let data = data_dir().unwrap();
78        assert!(
79            config.starts_with(&home),
80            "config_dir {config:?} should be under HOME {home:?}"
81        );
82        assert!(
83            data.starts_with(&home),
84            "data_dir {data:?} should be under HOME {home:?}"
85        );
86    }
87
88    #[test]
89    fn config_and_data_dirs_are_distinct_from_home() {
90        let home = home_dir().unwrap();
91        let config = config_dir().unwrap();
92        let data = data_dir().unwrap();
93        assert_ne!(config, home, "config_dir should not be HOME itself");
94        assert_ne!(data, home, "data_dir should not be HOME itself");
95    }
96}