Skip to main content

chkpt_core/
config.rs

1use std::path::{Path, PathBuf};
2
3/// Generate a 16-hex-char project ID from workspace path.
4pub 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
10/// Store directory layout for a project.
11pub 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    /// Tree path with 2-char prefix: trees/a3/rest_of_hash
70    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    /// Create all required directories.
76    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        // Prevent macOS Spotlight from indexing the store directory.
90        // mdworker_shared processes spike CPU after bulk writes without this.
91        #[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}