Skip to main content

podbox/config/
fs.rs

1use std::path::PathBuf;
2
3use anyhow::Result;
4
5pub fn expand_tilde(path: &str) -> PathBuf {
6    if let Some(stripped) = path.strip_prefix("~/") {
7        if let Some(home) = dirs::home_dir() {
8            return home.join(stripped);
9        }
10    }
11    if path == "~" {
12        if let Some(home) = dirs::home_dir() {
13            return home;
14        }
15    }
16    PathBuf::from(path)
17}
18
19pub fn config_dir() -> PathBuf {
20    dirs::config_dir()
21        .map(|d| d.join("podbox"))
22        .unwrap_or_else(|| PathBuf::from("~/.config/podbox"))
23}
24
25pub fn find_definition() -> Option<PathBuf> {
26    let new_local = PathBuf::from(".podbox.toml");
27    if new_local.exists() {
28        return Some(new_local);
29    }
30
31    let old_local = PathBuf::from(".podmgr.toml");
32    if old_local.exists() {
33        eprintln!(
34            "Warning: '.podmgr.toml' found. Rename it to '.podbox.toml' to silence this warning."
35        );
36        return Some(old_local);
37    }
38
39    let config_dir = config_dir();
40
41    if config_dir.is_dir() {
42        let mut entries: Vec<_> = std::fs::read_dir(&config_dir)
43            .into_iter()
44            .flatten()
45            .filter_map(|e| e.ok())
46            .filter(|e| {
47                e.path()
48                    .extension()
49                    .map(|ext| ext == "toml")
50                    .unwrap_or(false)
51            })
52            .map(|e| e.path())
53            .collect();
54
55        entries.sort();
56        if !entries.is_empty() {
57            if entries.len() > 1 {
58                eprintln!(
59                    "Warning: multiple configuration files found in {}. Selecting '{}' alphabetically. Use --config to specify a different file.",
60                    config_dir.display(),
61                    entries[0].display()
62                );
63            }
64            return Some(entries.remove(0));
65        }
66    }
67
68    None
69}
70
71pub fn list_configs() -> Vec<PathBuf> {
72    let config_dir = config_dir();
73    if !config_dir.is_dir() {
74        return vec![];
75    }
76    let mut entries: Vec<_> = std::fs::read_dir(&config_dir)
77        .into_iter()
78        .flatten()
79        .filter_map(|e| e.ok())
80        .filter(|e| {
81            e.path()
82                .extension()
83                .map(|ext| ext == "toml")
84                .unwrap_or(false)
85        })
86        .map(|e| e.path())
87        .collect();
88    entries.sort();
89    entries
90}
91
92pub fn active_context_path() -> PathBuf {
93    config_dir().join(".active")
94}
95
96pub fn read_active_context() -> Option<String> {
97    let path = active_context_path();
98    let content = std::fs::read_to_string(&path).ok()?;
99    let name = content.trim().to_string();
100    if name.is_empty() {
101        let _ = std::fs::remove_file(&path);
102        return None;
103    }
104    let config_path = config_dir().join(format!("{}.toml", name));
105    if config_path.exists() {
106        Some(name)
107    } else {
108        let _ = std::fs::remove_file(&path);
109        None
110    }
111}
112
113pub fn write_active_context(name: &str) -> Result<()> {
114    let path = active_context_path();
115    std::fs::create_dir_all(path.parent().unwrap())?;
116    std::fs::write(&path, name)?;
117    Ok(())
118}
119
120pub fn clear_active_context() -> Result<()> {
121    let path = active_context_path();
122    if path.exists() {
123        std::fs::remove_file(&path)?;
124    }
125    Ok(())
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_expand_tilde() {
134        let home = dirs::home_dir().unwrap();
135        assert_eq!(expand_tilde("~/foo"), home.join("foo"));
136        assert_eq!(expand_tilde("~"), home.clone());
137        assert_eq!(expand_tilde("/foo/bar"), PathBuf::from("/foo/bar"));
138    }
139}