Skip to main content

haematite_cli/serve/storage/
branches.rs

1//! Fork origins are recorded at the branch's immutable roots.
2
3use haematite::branch::ForkWrite;
4use serde::{Deserialize, Serialize};
5
6use super::{Captured, Failure, Storage, identifier, reads};
7use crate::serve::protocol::Version;
8
9const PROVENANCE_KEY: &[u8] = b"b\0fork";
10
11#[derive(Debug, Serialize, Deserialize)]
12#[serde(deny_unknown_fields)]
13pub struct ForkProvenance {
14    pub branch: String,
15    pub version: Version,
16}
17
18pub(super) fn provenance_write(
19    writer: &haematite::db::branches::BranchDatabase,
20    branch: &str,
21    at: Version,
22) -> Result<ForkWrite, Failure> {
23    Ok(ForkWrite {
24        shard_id: writer.shard_for(PROVENANCE_KEY),
25        key: PROVENANCE_KEY.to_vec(),
26        value: Some(
27            serde_json::to_vec(&ForkProvenance {
28                branch: branch.to_owned(),
29                version: at,
30            })
31            .map_err(Failure::storage)?,
32        ),
33    })
34}
35
36pub(super) fn read_provenance(
37    reader: &haematite::db::ReadOnlyDatabase,
38    at: &Captured,
39) -> Result<ForkProvenance, Failure> {
40    provenance(reader, at)?.ok_or_else(|| {
41        Failure::new(
42            "fork_provenance_missing",
43            "branch has no recorded fork origin",
44        )
45    })
46}
47
48/// The fork origin recorded at `at`'s roots, or `None` for a branch that was
49/// not forked (STOR-003 HR1: a release reads it for every live branch).
50pub(super) fn provenance(
51    reader: &haematite::db::ReadOnlyDatabase,
52    at: &Captured,
53) -> Result<Option<ForkProvenance>, Failure> {
54    let Some(bytes) = reads::get_bytes(reader, at, PROVENANCE_KEY)? else {
55        return Ok(None);
56    };
57    let provenance: ForkProvenance = serde_json::from_slice(&bytes).map_err(Failure::storage)?;
58    identifier(&provenance.branch)?;
59    Ok(Some(provenance))
60}
61
62impl Storage {
63    /// Read the explicit fork origin at the selected immutable roots.
64    ///
65    /// # Errors
66    /// Refuses missing or invalid provenance and propagates storage failures.
67    pub fn fork_provenance(&self, at: &Captured) -> Result<ForkProvenance, Failure> {
68        read_provenance(&self.reader, at)
69    }
70}