Skip to main content

osdk_core/store/
manifest.rs

1//! Per-install manifest: records every file materialized into an install dir,
2//! along with its content hash, mode, and (for symlinks) target. Used to
3//! verify installs and to compute the live set for store GC.
4
5use std::path::{Path, PathBuf};
6
7use serde::{Deserialize, Serialize};
8
9use crate::error::{Error, Result};
10
11/// Name of the manifest file written at the root of each install dir.
12pub const MANIFEST_FILE: &str = ".osdk-manifest.json";
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct FileEntry {
16    /// Path relative to the install root, using forward slashes.
17    pub path: String,
18    /// blake3 content hash (hex), for regular files. None for symlinks/dirs.
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub hash: Option<String>,
21    /// Unix mode bits (permissions). 0 if unknown / not applicable.
22    #[serde(default)]
23    pub mode: u32,
24    /// For symlink entries: the link target (verbatim).
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub symlink: Option<String>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct Manifest {
31    pub tool: String,
32    pub version: String,
33    /// Which link mode was used to materialize this install.
34    pub link_mode: String,
35    pub files: Vec<FileEntry>,
36}
37
38impl Manifest {
39    pub fn new(
40        tool: impl Into<String>,
41        version: impl Into<String>,
42        link_mode: impl Into<String>,
43    ) -> Manifest {
44        Manifest {
45            tool: tool.into(),
46            version: version.into(),
47            link_mode: link_mode.into(),
48            files: Vec::new(),
49        }
50    }
51
52    pub fn manifest_path(install_dir: &Path) -> PathBuf {
53        install_dir.join(MANIFEST_FILE)
54    }
55
56    pub fn load(install_dir: &Path) -> Result<Manifest> {
57        let p = Self::manifest_path(install_dir);
58        let bytes = std::fs::read(&p).map_err(|e| Error::io(&p, e))?;
59        Ok(serde_json::from_slice(&bytes)?)
60    }
61
62    pub fn save(&self, install_dir: &Path) -> Result<()> {
63        let p = Self::manifest_path(install_dir);
64        let bytes = serde_json::to_vec_pretty(self)?;
65        std::fs::write(&p, bytes).map_err(|e| Error::io(&p, e))?;
66        Ok(())
67    }
68
69    /// The set of store hashes referenced by this install.
70    pub fn referenced_hashes(&self) -> impl Iterator<Item = &str> {
71        self.files.iter().filter_map(|f| f.hash.as_deref())
72    }
73}