Skip to main content

algocline_core/
app_dir.rs

1//! Application directory layout for algocline.
2//!
3//! [`AppDir`] encapsulates the root directory (`~/.algocline/` by default,
4//! overridable via the `ALC_HOME` environment variable) and provides typed
5//! path accessors for each subsystem directory. Construction is expected to
6//! happen in a single resolution point (`AppConfig::from_env` in the app
7//! crate); downstream Service-layer code reaches these paths through the
8//! accessors here rather than reading `HOME` / `ALC_HOME` directly.
9
10use std::path::{Path, PathBuf};
11use std::sync::Arc;
12
13/// Application root directory.
14///
15/// The inner `root` is stored behind an [`Arc`] so `AppDir::clone` is
16/// `O(1)` (refcount bump, no path allocation). Downstream services call
17/// `clone()` on `AppDir` freely as part of per-request delegate
18/// construction (see `FsInstalledManifestStore` and siblings in `algocline-app`);
19/// keeping clone cheap avoids the per-call `PathBuf` allocation that an
20/// owned `PathBuf` field would incur.
21#[derive(Clone, Debug)]
22pub struct AppDir {
23    root: Arc<PathBuf>,
24}
25
26impl AppDir {
27    pub fn new(root: PathBuf) -> Self {
28        Self {
29            root: Arc::new(root),
30        }
31    }
32
33    pub fn root(&self) -> &Path {
34        &self.root
35    }
36
37    pub fn packages_dir(&self) -> PathBuf {
38        self.root.join("packages")
39    }
40
41    pub fn cards_dir(&self) -> PathBuf {
42        self.root.join("cards")
43    }
44
45    pub fn state_dir(&self) -> PathBuf {
46        self.root.join("state")
47    }
48
49    pub fn evals_dir(&self) -> PathBuf {
50        self.root.join("evals")
51    }
52
53    pub fn logs_dir(&self) -> PathBuf {
54        self.root.join("logs")
55    }
56
57    pub fn scenarios_dir(&self) -> PathBuf {
58        self.root.join("scenarios")
59    }
60
61    pub fn types_dir(&self) -> PathBuf {
62        self.root.join("types")
63    }
64
65    pub fn hub_cache_dir(&self) -> PathBuf {
66        self.root.join("hub_cache")
67    }
68
69    pub fn installed_json(&self) -> PathBuf {
70        self.root.join("installed.json")
71    }
72
73    pub fn hub_registries_json(&self) -> PathBuf {
74        self.root.join("hub_registries.json")
75    }
76
77    pub fn config_toml(&self) -> PathBuf {
78        self.root.join("config.toml")
79    }
80
81    pub fn init_lua(&self) -> PathBuf {
82        self.root.join("init.lua")
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn joins_each_subdir_under_root() {
92        let dir = AppDir::new(PathBuf::from("/tmp/alc-root"));
93        assert_eq!(dir.root(), Path::new("/tmp/alc-root"));
94        assert_eq!(dir.packages_dir(), PathBuf::from("/tmp/alc-root/packages"));
95        assert_eq!(dir.cards_dir(), PathBuf::from("/tmp/alc-root/cards"));
96        assert_eq!(dir.state_dir(), PathBuf::from("/tmp/alc-root/state"));
97        assert_eq!(dir.evals_dir(), PathBuf::from("/tmp/alc-root/evals"));
98        assert_eq!(dir.logs_dir(), PathBuf::from("/tmp/alc-root/logs"));
99        assert_eq!(
100            dir.scenarios_dir(),
101            PathBuf::from("/tmp/alc-root/scenarios")
102        );
103        assert_eq!(dir.types_dir(), PathBuf::from("/tmp/alc-root/types"));
104        assert_eq!(
105            dir.hub_cache_dir(),
106            PathBuf::from("/tmp/alc-root/hub_cache")
107        );
108        assert_eq!(
109            dir.installed_json(),
110            PathBuf::from("/tmp/alc-root/installed.json")
111        );
112        assert_eq!(
113            dir.hub_registries_json(),
114            PathBuf::from("/tmp/alc-root/hub_registries.json")
115        );
116        assert_eq!(
117            dir.config_toml(),
118            PathBuf::from("/tmp/alc-root/config.toml")
119        );
120        assert_eq!(dir.init_lua(), PathBuf::from("/tmp/alc-root/init.lua"));
121    }
122
123    #[test]
124    fn clone_is_independent() {
125        let a = AppDir::new(PathBuf::from("/a"));
126        let b = a.clone();
127        assert_eq!(a.root(), b.root());
128    }
129}