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    /// Store root for `alc.nn` model bundles (`<root>/nn`).
70    ///
71    /// Consumed by the engine bridge under the `nn` feature to construct the
72    /// filesystem-backed `NnStore` that `alc.nn.save` / `alc.nn.load` resolve
73    /// through. Kept alongside the other subsystem accessors so the engine
74    /// never inspects `$HOME` / `$ALC_HOME` directly (see crate docs).
75    pub fn nn_dir(&self) -> PathBuf {
76        self.root.join("nn")
77    }
78
79    pub fn installed_json(&self) -> PathBuf {
80        self.root.join("installed.json")
81    }
82
83    pub fn hub_registries_json(&self) -> PathBuf {
84        self.root.join("hub_registries.json")
85    }
86
87    pub fn config_toml(&self) -> PathBuf {
88        self.root.join("config.toml")
89    }
90
91    pub fn init_lua(&self) -> PathBuf {
92        self.root.join("init.lua")
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn joins_each_subdir_under_root() {
102        let dir = AppDir::new(PathBuf::from("/tmp/alc-root"));
103        assert_eq!(dir.root(), Path::new("/tmp/alc-root"));
104        assert_eq!(dir.packages_dir(), PathBuf::from("/tmp/alc-root/packages"));
105        assert_eq!(dir.cards_dir(), PathBuf::from("/tmp/alc-root/cards"));
106        assert_eq!(dir.state_dir(), PathBuf::from("/tmp/alc-root/state"));
107        assert_eq!(dir.evals_dir(), PathBuf::from("/tmp/alc-root/evals"));
108        assert_eq!(dir.logs_dir(), PathBuf::from("/tmp/alc-root/logs"));
109        assert_eq!(
110            dir.scenarios_dir(),
111            PathBuf::from("/tmp/alc-root/scenarios")
112        );
113        assert_eq!(dir.types_dir(), PathBuf::from("/tmp/alc-root/types"));
114        assert_eq!(
115            dir.hub_cache_dir(),
116            PathBuf::from("/tmp/alc-root/hub_cache")
117        );
118        assert_eq!(dir.nn_dir(), PathBuf::from("/tmp/alc-root/nn"));
119        assert_eq!(
120            dir.installed_json(),
121            PathBuf::from("/tmp/alc-root/installed.json")
122        );
123        assert_eq!(
124            dir.hub_registries_json(),
125            PathBuf::from("/tmp/alc-root/hub_registries.json")
126        );
127        assert_eq!(
128            dir.config_toml(),
129            PathBuf::from("/tmp/alc-root/config.toml")
130        );
131        assert_eq!(dir.init_lua(), PathBuf::from("/tmp/alc-root/init.lua"));
132    }
133
134    #[test]
135    fn clone_is_independent() {
136        let a = AppDir::new(PathBuf::from("/a"));
137        let b = a.clone();
138        assert_eq!(a.root(), b.root());
139    }
140}