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 catalog_path(&self) -> PathBuf {
38 self.base.join("catalog.sqlite")
39 }
40
41 pub fn trees_dir(&self) -> PathBuf {
42 self.base.join("trees")
43 }
44
45 pub fn packs_dir(&self) -> PathBuf {
46 self.base.join("packs")
47 }
48
49 pub fn index_path(&self) -> PathBuf {
50 self.base.join("index.bin")
51 }
52
53 pub fn locks_dir(&self) -> PathBuf {
54 self.base.join("locks")
55 }
56
57 pub fn ensure_dirs(&self) -> std::io::Result<()> {
59 for dir in [
60 self.base.clone(),
61 self.trees_dir(),
62 self.packs_dir(),
63 self.locks_dir(),
64 ] {
65 std::fs::create_dir_all(dir)?;
66 }
67
68 #[cfg(target_os = "macos")]
71 {
72 let marker = self.base.join(".metadata_never_index");
73 if !marker.exists() {
74 std::fs::File::create(marker)?;
75 }
76 }
77
78 Ok(())
79 }
80}