1use std::path::{Path, PathBuf};
2
3pub fn project_id_from_path(path: &Path) -> String {
5 let canonical = path.to_string_lossy();
6 let hash = xxhash_rust::xxh3::xxh3_128(canonical.as_bytes());
7 format!("{:032x}", hash)[..16].to_string()
8}
9
10pub struct StoreLayout {
12 base: PathBuf,
13}
14
15impl StoreLayout {
16 pub fn new(project_id: &str) -> Self {
17 let home_dir = std::env::var_os("CHKPT_HOME")
18 .map(PathBuf::from)
19 .or_else(dirs::home_dir)
20 .unwrap_or_else(|| PathBuf::from("."));
21 Self::from_home_dir(home_dir, project_id)
22 }
23
24 pub fn from_home_dir<P: AsRef<Path>>(home_dir: P, project_id: &str) -> Self {
25 let base = home_dir
26 .as_ref()
27 .join(".chkpt")
28 .join("stores")
29 .join(project_id);
30 Self { base }
31 }
32
33 pub fn base_dir(&self) -> &Path {
34 &self.base
35 }
36
37 pub fn snapshots_dir(&self) -> PathBuf {
38 self.base.join("snapshots")
39 }
40
41 pub fn catalog_path(&self) -> PathBuf {
42 self.base.join("catalog.sqlite")
43 }
44
45 pub fn trees_dir(&self) -> PathBuf {
46 self.base.join("trees")
47 }
48
49 pub fn packs_dir(&self) -> PathBuf {
50 self.base.join("packs")
51 }
52
53 pub fn index_path(&self) -> PathBuf {
54 self.base.join("index.bin")
55 }
56
57 pub fn locks_dir(&self) -> PathBuf {
58 self.base.join("locks")
59 }
60
61 pub fn attachments_deps_dir(&self) -> PathBuf {
62 self.base.join("attachments").join("deps")
63 }
64
65 pub fn attachments_git_dir(&self) -> PathBuf {
66 self.base.join("attachments").join("git")
67 }
68
69 pub fn tree_path(&self, hash_hex: &str) -> PathBuf {
71 let (prefix, rest) = hash_hex.split_at(2);
72 self.base.join("trees").join(prefix).join(rest)
73 }
74
75 pub fn ensure_dirs(&self) -> std::io::Result<()> {
77 for dir in [
78 self.base.clone(),
79 self.snapshots_dir(),
80 self.trees_dir(),
81 self.packs_dir(),
82 self.locks_dir(),
83 self.attachments_deps_dir(),
84 self.attachments_git_dir(),
85 ] {
86 std::fs::create_dir_all(dir)?;
87 }
88
89 #[cfg(target_os = "macos")]
92 {
93 let marker = self.base.join(".metadata_never_index");
94 if !marker.exists() {
95 std::fs::File::create(marker)?;
96 }
97 }
98
99 Ok(())
100 }
101}