1use std::path::{Path, PathBuf};
2
3#[derive(Debug, Clone)]
5pub struct EddaPaths {
6 pub root: PathBuf,
7 pub edda_dir: PathBuf,
8 pub ledger_dir: PathBuf,
9 pub ledger_db: PathBuf,
10 pub blobs_dir: PathBuf,
11 pub branches_dir: PathBuf,
12 pub drafts_dir: PathBuf,
13 pub lock_file: PathBuf,
14 pub config_json: PathBuf,
15 pub patterns_dir: PathBuf,
16 pub blob_meta_json: PathBuf,
17 pub tombstones_jsonl: PathBuf,
18 pub archive_dir: PathBuf,
19 pub archive_blobs_dir: PathBuf,
20}
21
22impl EddaPaths {
23 pub fn discover(repo_root: impl Into<PathBuf>) -> Self {
25 let root = repo_root.into();
26 let edda_dir = root.join(".edda");
27 let ledger_dir = edda_dir.join("ledger");
28 let archive_dir = edda_dir.join("archive");
29 Self {
30 ledger_db: edda_dir.join("ledger.db"),
31 blobs_dir: ledger_dir.join("blobs"),
32 blob_meta_json: ledger_dir.join("blob_meta.json"),
33 tombstones_jsonl: ledger_dir.join("tombstones.jsonl"),
34 branches_dir: edda_dir.join("branches"),
35 drafts_dir: edda_dir.join("drafts"),
36 lock_file: edda_dir.join("LOCK"),
37 config_json: edda_dir.join("config.json"),
38 patterns_dir: edda_dir.join("patterns"),
39 archive_blobs_dir: archive_dir.join("blobs"),
40 archive_dir,
41 ledger_dir,
42 edda_dir,
43 root,
44 }
45 }
46
47 pub fn ensure_layout(&self) -> anyhow::Result<()> {
49 for dir in [
50 &self.ledger_dir,
51 &self.blobs_dir,
52 &self.branches_dir,
53 &self.drafts_dir,
54 &self.patterns_dir,
55 ] {
56 std::fs::create_dir_all(dir)?;
57 }
58 Ok(())
59 }
60
61 pub fn is_initialized(&self) -> bool {
63 self.edda_dir.is_dir()
64 }
65
66 pub fn branch_dir(&self, name: &str) -> PathBuf {
68 self.branches_dir.join(name)
69 }
70}
71
72impl EddaPaths {
73 pub fn find_root(start: &Path) -> Option<PathBuf> {
76 let mut cur = start.to_path_buf();
77 loop {
78 if cur.join(".edda").is_dir() {
79 return Some(cur);
80 }
81 if !cur.pop() {
82 return None;
83 }
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn discover_builds_correct_paths() {
94 let p = EddaPaths::discover("/tmp/repo");
95 assert_eq!(p.edda_dir, PathBuf::from("/tmp/repo/.edda"));
96 assert_eq!(p.blobs_dir, PathBuf::from("/tmp/repo/.edda/ledger/blobs"));
97 assert_eq!(p.lock_file, PathBuf::from("/tmp/repo/.edda/LOCK"));
98 assert_eq!(p.patterns_dir, PathBuf::from("/tmp/repo/.edda/patterns"));
99 assert_eq!(
100 p.blob_meta_json,
101 PathBuf::from("/tmp/repo/.edda/ledger/blob_meta.json")
102 );
103 assert_eq!(
104 p.tombstones_jsonl,
105 PathBuf::from("/tmp/repo/.edda/ledger/tombstones.jsonl")
106 );
107 assert_eq!(p.archive_dir, PathBuf::from("/tmp/repo/.edda/archive"));
108 assert_eq!(
109 p.archive_blobs_dir,
110 PathBuf::from("/tmp/repo/.edda/archive/blobs")
111 );
112 }
113
114 #[test]
115 fn ensure_layout_creates_dirs() {
116 let tmp = std::env::temp_dir().join(format!("edda_test_{}", std::process::id()));
117 let _ = std::fs::remove_dir_all(&tmp);
118 let p = EddaPaths::discover(&tmp);
119 p.ensure_layout().unwrap();
120 assert!(p.ledger_dir.is_dir());
121 assert!(p.blobs_dir.is_dir());
122 assert!(p.branches_dir.is_dir());
123 assert!(p.drafts_dir.is_dir());
124 assert!(p.patterns_dir.is_dir());
125 let _ = std::fs::remove_dir_all(&tmp);
126 }
127}