Skip to main content

capo_agent/
paths.rs

1//! Filesystem path helpers for capo's per-user agent directory.
2//!
3//! Mirrors pi's layout: `~/.capo/agent/` is the global app dir
4//! (overridable via `CAPO_AGENT_DIR`), and everything else
5//! (sessions, settings, auth, permissions) lives underneath.
6
7use std::env;
8use std::path::PathBuf;
9
10/// Resolve the per-user agent directory.
11///
12/// Priority:
13/// 1. `$CAPO_AGENT_DIR` env var if set (leading `~/` expanded to `$HOME`).
14/// 2. `$HOME/.capo/agent`.
15/// 3. `./.capo/agent` as a last-resort fallback if `$HOME` is missing.
16pub fn agent_dir() -> PathBuf {
17    agent_dir_with(|name| env::var(name).ok())
18}
19
20/// Test seam: lookup env vars through a closure. Production calls
21/// [`agent_dir`] which delegates to `env::var`.
22pub fn agent_dir_with<F>(lookup: F) -> PathBuf
23where
24    F: Fn(&str) -> Option<String>,
25{
26    if let Some(raw) = lookup("CAPO_AGENT_DIR") {
27        return expand_tilde(&raw, &lookup);
28    }
29    if let Some(home) = lookup("HOME") {
30        return PathBuf::from(home).join(".capo").join("agent");
31    }
32    PathBuf::from(".capo").join("agent")
33}
34
35fn expand_tilde<F>(raw: &str, lookup: &F) -> PathBuf
36where
37    F: Fn(&str) -> Option<String>,
38{
39    let trimmed = raw.trim();
40    if trimmed == "~" {
41        return lookup("HOME")
42            .map(PathBuf::from)
43            .unwrap_or_else(|| PathBuf::from("~"));
44    }
45    if let Some(rest) = trimmed.strip_prefix("~/") {
46        if let Some(home) = lookup("HOME") {
47            return PathBuf::from(home).join(rest);
48        }
49    }
50    PathBuf::from(trimmed)
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use std::collections::HashMap;
57
58    fn lookup(map: HashMap<&'static str, &'static str>) -> impl Fn(&str) -> Option<String> {
59        move |name| map.get(name).map(|v| (*v).to_string())
60    }
61
62    #[test]
63    fn agent_dir_uses_capo_agent_dir_env() {
64        let env = HashMap::from([("CAPO_AGENT_DIR", "/tmp/x")]);
65        assert_eq!(agent_dir_with(lookup(env)), PathBuf::from("/tmp/x"));
66    }
67
68    #[test]
69    fn agent_dir_expands_tilde_in_capo_agent_dir() {
70        let env = HashMap::from([("CAPO_AGENT_DIR", "~/.test-capo"), ("HOME", "/Users/wade")]);
71        assert_eq!(
72            agent_dir_with(lookup(env)),
73            PathBuf::from("/Users/wade/.test-capo")
74        );
75    }
76
77    #[test]
78    fn agent_dir_defaults_to_home_capo_agent() {
79        let env = HashMap::from([("HOME", "/Users/wade")]);
80        assert_eq!(
81            agent_dir_with(lookup(env)),
82            PathBuf::from("/Users/wade/.capo/agent")
83        );
84    }
85}