Skip to main content

bv_core/
cache.rs

1use std::path::PathBuf;
2
3/// On-disk layout for bv's local cache.
4pub struct CacheLayout {
5    root: PathBuf,
6}
7
8impl CacheLayout {
9    /// Resolve the cache root.
10    ///
11    /// Priority:
12    /// 1. `BV_CACHE_DIR` env var (used by tests and CI for isolation)
13    /// 2. `$XDG_CACHE_HOME/bv` if `XDG_CACHE_HOME` is set
14    /// 3. `~/.cache/bv` (consistent across Linux and macOS)
15    pub fn new() -> Self {
16        if let Ok(dir) = std::env::var("BV_CACHE_DIR") {
17            return Self::with_root(PathBuf::from(dir));
18        }
19        let root = xdg_cache_home().join("bv");
20        Self { root }
21    }
22
23    pub fn with_root(root: PathBuf) -> Self {
24        Self { root }
25    }
26
27    pub fn root(&self) -> &PathBuf {
28        &self.root
29    }
30
31    /// `<root>/images/<digest>/` - metadata we track alongside runtime storage.
32    pub fn image_dir(&self, digest: &str) -> PathBuf {
33        self.root.join("images").join(digest)
34    }
35
36    /// `<root>/tools/<tool_id>/<version>/manifest.toml`
37    pub fn manifest_path(&self, tool_id: &str, version: &str) -> PathBuf {
38        self.root
39            .join("tools")
40            .join(tool_id)
41            .join(version)
42            .join("manifest.toml")
43    }
44
45    /// `<root>/index/<index_name>/` - local clones of git registries.
46    pub fn index_dir(&self, index_name: &str) -> PathBuf {
47        self.root.join("index").join(index_name)
48    }
49
50    /// `<root>/data/<dataset_id>/<version>/`
51    pub fn data_dir(&self, dataset_id: &str, version: &str) -> PathBuf {
52        self.root.join("data").join(dataset_id).join(version)
53    }
54
55    /// `<root>/sif/` - Apptainer SIF image cache.
56    pub fn sif_dir(&self) -> PathBuf {
57        self.root.join("sif")
58    }
59
60    /// `<root>/tmp/` - staging area for atomic writes.
61    pub fn tmp_dir(&self) -> PathBuf {
62        self.root.join("tmp")
63    }
64
65    /// `<root>/owned-images.txt` - persistent record of every image bv has pulled.
66    /// Used by `bv cache prune` to identify bv-owned Docker images.
67    pub fn owned_images_path(&self) -> PathBuf {
68        self.root.join("owned-images.txt")
69    }
70}
71
72impl Default for CacheLayout {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77
78fn xdg_cache_home() -> PathBuf {
79    if let Ok(dir) = std::env::var("XDG_CACHE_HOME") {
80        return PathBuf::from(dir);
81    }
82    // Fall back to ~/.cache on all platforms.
83    let home = std::env::var_os("HOME")
84        .map(PathBuf::from)
85        .unwrap_or_else(|| PathBuf::from("."));
86    home.join(".cache")
87}