tl_cli/
paths.rs

1//! XDG-style path utilities for configuration and cache directories.
2//!
3//! This module provides consistent path resolution across platforms,
4//! preferring XDG Base Directory Specification conventions over
5//! OS-specific locations.
6
7use anyhow::{Context, Result};
8use std::path::PathBuf;
9
10/// Returns the configuration directory for tl.
11///
12/// Resolution order:
13/// 1. `$XDG_CONFIG_HOME/tl` if `XDG_CONFIG_HOME` is set
14/// 2. `~/.config/tl` otherwise
15///
16/// # Errors
17///
18/// Returns an error if the home directory cannot be determined.
19pub fn config_dir() -> Result<PathBuf> {
20    match std::env::var("XDG_CONFIG_HOME") {
21        Ok(xdg) => Ok(PathBuf::from(xdg).join("tl")),
22        Err(_) => Ok(home_dir()?.join(".config").join("tl")),
23    }
24}
25
26/// Returns the cache directory for tl.
27///
28/// Resolution order:
29/// 1. `$XDG_CACHE_HOME/tl` if `XDG_CACHE_HOME` is set
30/// 2. `~/.cache/tl` otherwise
31///
32/// # Errors
33///
34/// Returns an error if the home directory cannot be determined.
35pub fn cache_dir() -> Result<PathBuf> {
36    match std::env::var("XDG_CACHE_HOME") {
37        Ok(xdg) => Ok(PathBuf::from(xdg).join("tl")),
38        Err(_) => Ok(home_dir()?.join(".cache").join("tl")),
39    }
40}
41
42/// Returns the user's home directory.
43///
44/// # Errors
45///
46/// Returns an error if the home directory cannot be determined.
47fn home_dir() -> Result<PathBuf> {
48    dirs::home_dir().context("Failed to determine home directory")
49}
50
51#[cfg(test)]
52#[allow(clippy::unwrap_used)]
53mod tests {
54    use super::*;
55    use serial_test::serial;
56
57    #[test]
58    #[serial]
59    fn test_config_dir_default() {
60        // Clear XDG_CONFIG_HOME to test default behavior
61        let original = std::env::var("XDG_CONFIG_HOME").ok();
62        unsafe { std::env::remove_var("XDG_CONFIG_HOME") };
63
64        let dir = config_dir().unwrap();
65        assert!(dir.ends_with(".config/tl"));
66
67        // Restore
68        if let Some(val) = original {
69            unsafe { std::env::set_var("XDG_CONFIG_HOME", val) };
70        }
71    }
72
73    #[test]
74    #[serial]
75    fn test_config_dir_xdg_override() {
76        let original = std::env::var("XDG_CONFIG_HOME").ok();
77        unsafe { std::env::set_var("XDG_CONFIG_HOME", "/custom/config") };
78
79        let dir = config_dir().unwrap();
80        assert_eq!(dir, PathBuf::from("/custom/config/tl"));
81
82        // Restore
83        if let Some(val) = original {
84            unsafe { std::env::set_var("XDG_CONFIG_HOME", val) };
85        } else {
86            unsafe { std::env::remove_var("XDG_CONFIG_HOME") };
87        }
88    }
89
90    #[test]
91    #[serial]
92    fn test_cache_dir_default() {
93        // Clear XDG_CACHE_HOME to test default behavior
94        let original = std::env::var("XDG_CACHE_HOME").ok();
95        unsafe { std::env::remove_var("XDG_CACHE_HOME") };
96
97        let dir = cache_dir().unwrap();
98        assert!(dir.ends_with(".cache/tl"));
99
100        // Restore
101        if let Some(val) = original {
102            unsafe { std::env::set_var("XDG_CACHE_HOME", val) };
103        }
104    }
105
106    #[test]
107    #[serial]
108    fn test_cache_dir_xdg_override() {
109        let original = std::env::var("XDG_CACHE_HOME").ok();
110        unsafe { std::env::set_var("XDG_CACHE_HOME", "/custom/cache") };
111
112        let dir = cache_dir().unwrap();
113        assert_eq!(dir, PathBuf::from("/custom/cache/tl"));
114
115        // Restore
116        if let Some(val) = original {
117            unsafe { std::env::set_var("XDG_CACHE_HOME", val) };
118        } else {
119            unsafe { std::env::remove_var("XDG_CACHE_HOME") };
120        }
121    }
122}