Skip to main content

ao_core/
paths.rs

1//! Disk layout helpers for the `~/.ao-rs/` data dir.
2//!
3//! Equivalent of `packages/core/src/paths.ts` in the reference repo, scoped
4//! down to what ao-rs features actually use today. Keep this module minimal:
5//! add a helper when a feature needs a new on-disk location, not before.
6
7use std::path::PathBuf;
8
9/// Root of the ao-rs data directory: `~/.ao-rs`.
10pub fn data_dir() -> PathBuf {
11    let home = std::env::var("HOME")
12        .map(PathBuf::from)
13        .unwrap_or_else(|_| PathBuf::from("/tmp"));
14    home.join(".ao-rs")
15}
16
17/// `~/.ao-rs/sessions` — where `SessionManager` stores per-project session yaml files.
18pub fn default_sessions_dir() -> PathBuf {
19    data_dir().join("sessions")
20}
21
22/// `~/.ao-rs/lifecycle.pid` — pidfile used by `ao-rs watch` to coordinate a
23/// singleton daemon process. See `lockfile.rs` and `packages/cli/src/lib/lifecycle-service.ts`
24/// in the reference repo.
25pub fn lifecycle_pid_file() -> PathBuf {
26    data_dir().join("lifecycle.pid")
27}
28
29/// `~/.ao-rs/cost-ledger/` — monthly-rotated cost ledger files.
30/// See `cost_ledger.rs` for the `YYYY-MM.yaml` layout.
31pub fn cost_ledger_dir() -> PathBuf {
32    data_dir().join("cost-ledger")
33}
34
35/// `~/.ao-rs/review-fingerprints/` — per-session fingerprints used by
36/// `ao-rs review-check` to detect new PR comments since the last run.
37pub fn review_fingerprint_dir() -> PathBuf {
38    data_dir().join("review-fingerprints")
39}
40
41/// `~/.ao-rs/review-fingerprints/{session_id}.txt` — fingerprint file for
42/// a single session.
43pub fn review_fingerprint_file(session_id: &str) -> PathBuf {
44    review_fingerprint_dir().join(format!("{session_id}.txt"))
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn data_dir_ends_with_dot_ao_rs() {
53        let p = data_dir();
54        assert_eq!(p.file_name().and_then(|s| s.to_str()), Some(".ao-rs"));
55    }
56
57    #[test]
58    fn default_sessions_dir_is_under_data_dir() {
59        let p = default_sessions_dir();
60        assert_eq!(p.parent(), Some(data_dir().as_path()));
61        assert_eq!(p.file_name().and_then(|s| s.to_str()), Some("sessions"));
62    }
63
64    #[test]
65    fn lifecycle_pid_file_is_under_data_dir() {
66        let p = lifecycle_pid_file();
67        assert_eq!(p.parent(), Some(data_dir().as_path()));
68        assert_eq!(
69            p.file_name().and_then(|s| s.to_str()),
70            Some("lifecycle.pid")
71        );
72    }
73
74    #[test]
75    fn cost_ledger_dir_is_under_data_dir() {
76        let p = cost_ledger_dir();
77        assert_eq!(p.parent(), Some(data_dir().as_path()));
78        assert_eq!(p.file_name().and_then(|s| s.to_str()), Some("cost-ledger"));
79    }
80
81    #[test]
82    fn review_fingerprint_dir_is_under_data_dir() {
83        let p = review_fingerprint_dir();
84        assert_eq!(p.parent(), Some(data_dir().as_path()));
85        assert_eq!(
86            p.file_name().and_then(|s| s.to_str()),
87            Some("review-fingerprints")
88        );
89    }
90
91    #[test]
92    fn review_fingerprint_file_is_under_fingerprint_dir() {
93        let p = review_fingerprint_file("abcd-1234");
94        assert_eq!(p.parent(), Some(review_fingerprint_dir().as_path()));
95        assert_eq!(
96            p.file_name().and_then(|s| s.to_str()),
97            Some("abcd-1234.txt")
98        );
99    }
100
101    #[test]
102    fn review_fingerprint_file_respects_arbitrary_session_id() {
103        // Session ids are UUIDs in practice; the helper shouldn't mangle them.
104        let id = "9f2c5a0e-d8f4-4b2a-9e7c-123456789abc";
105        let p = review_fingerprint_file(id);
106        assert_eq!(
107            p.file_name().and_then(|s| s.to_str()),
108            Some("9f2c5a0e-d8f4-4b2a-9e7c-123456789abc.txt")
109        );
110    }
111}