Skip to main content

rootcx_platform/
dirs.rs

1use std::path::PathBuf;
2use crate::PlatformError;
3
4pub fn home_dir() -> Result<PathBuf, PlatformError> {
5    #[cfg(unix)]
6    { std::env::var_os("HOME").map(PathBuf::from).ok_or(PlatformError("home directory")) }
7    #[cfg(windows)]
8    { std::env::var_os("USERPROFILE").map(PathBuf::from).ok_or(PlatformError("home directory")) }
9}
10
11pub fn data_dir() -> Result<PathBuf, PlatformError> {
12    #[cfg(target_os = "macos")]
13    { return home_dir().map(|h| h.join("Library/Application Support/RootCX")); }
14    #[cfg(target_os = "linux")]
15    {
16        if let Some(p) = std::env::var_os("XDG_DATA_HOME") { return Ok(PathBuf::from(p).join("RootCX")); }
17        return home_dir().map(|h| h.join(".local/share/RootCX"));
18    }
19    #[cfg(windows)]
20    { std::env::var_os("APPDATA").map(|a| PathBuf::from(a).join("RootCX")).ok_or(PlatformError("APPDATA")) }
21}
22
23// macOS uses XDG-compatible path intentionally — avoids splitting config across
24// ~/Library/Preferences and ~/.config; consistent behaviour across all Unices.
25pub fn config_dir() -> Result<PathBuf, PlatformError> {
26    #[cfg(not(windows))]
27    {
28        if let Some(p) = std::env::var_os("XDG_CONFIG_HOME") { return Ok(PathBuf::from(p).join("rootcx")); }
29        return home_dir().map(|h| h.join(".config/rootcx"));
30    }
31    #[cfg(windows)]
32    { std::env::var_os("APPDATA").map(|a| PathBuf::from(a).join("rootcx")).ok_or(PlatformError("APPDATA")) }
33}
34
35pub fn rootcx_home() -> Result<PathBuf, PlatformError> {
36    home_dir().map(|h| h.join(".rootcx"))
37}
38
39// Resolution order: $ROOTCX_RESOURCES env > dev Cargo.toml dir > macOS bundle > exe-adjacent
40pub fn resources_dir(manifest_dir: &str) -> Result<PathBuf, PlatformError> {
41    if let Ok(p) = std::env::var("ROOTCX_RESOURCES") {
42        return std::fs::canonicalize(&p).map_err(|_| PlatformError("ROOTCX_RESOURCES path"));
43    }
44    let dev = PathBuf::from(manifest_dir).join("resources");
45    if dev.is_dir() { return Ok(dev); }
46    let exe = std::env::current_exe().map_err(|_| PlatformError("executable path"))?;
47    let dir = exe.parent().ok_or(PlatformError("executable parent"))?;
48    #[cfg(target_os = "macos")]
49    if let Some(contents) = dir.parent() {
50        let p = contents.join("Resources/resources");
51        if p.is_dir() { return Ok(p); }
52    }
53    Ok(dir.join("resources"))
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    #[test] fn home_resolves()    { home_dir().unwrap(); }
60    #[test] fn data_has_rootcx()  { assert!(data_dir().unwrap().to_string_lossy().contains("RootCX")); }
61    #[test] fn home_ends_rootcx() { assert!(rootcx_home().unwrap().ends_with(".rootcx")); }
62}