haematite-cli 0.21.8

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
//! F1 (ruled 5 Oct 08:55 and 09:03): a fork inherits its parent's companion,
//! so its first walk lists only the records that differ (F-1 to F-3).
//!
//! The parent comes from the fork's own provenance row. Nothing is carried
//! while any position in the parent's companion has a walk open: an open
//! walk's target is the parent's own version (F2b), and carried it would name
//! that version as the fork's. The answer is then `Waiting`, naming the
//! field.
//!
//! Step one publishes the fork's companion as a namespace record on the
//! parent's companion's shard roots, sharing its nodes, positions carried as
//! they are: each names the branch whose version its `through` is. Step two,
//! one commit, removes the position of every field the fork does not declare
//! at its head; that field's vectors stay for the pruning box (F-7), and with
//! no position nothing holds the parent's version for them. Both steps are
//! safe to repeat: a call finding the companion there does step two if any
//! such position is left, and answers `Already` only when none is. A
//! companion already there, inherited or of the fork's own making, is never
//! replaced.
//!
//! The parent's model and the fork's cannot differ: both read the same
//! declaration, and a declaration naming another model while the field has
//! vectors is refused (`embed_model_change_not_available`, Q7), so no check
//! is made here.
//!
//! Durable commits: one ref creation for step one, and at most one commit for
//! step two, only when the fork leaves a field undeclared.

use super::branches::provenance;
use super::embedded::{self, Field};
use super::{
    BranchKind, Captured, Failure, Storage, branch_name, create_branch_with_kind,
    current_timestamp, identifier, reads, version,
};

/// What one call did.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Inherited {
    /// The companion was made, or its positions for undeclared fields were
    /// removed.
    Done,
    /// The fork has its companion and nothing is left to do.
    Already,
    /// The branch was not forked, or its parent has no companion.
    Nothing,
    /// The parent's walk on this field is open; nothing was written.
    Waiting { collection: String, field: String },
}

impl Storage {
    /// Gives `fork` a companion made from its parent's (F1).
    ///
    /// # Errors
    /// `branch_missing`; whatever reading the provenance, a position or a
    /// declaration, making the companion or the commit answers.
    pub fn embed_inherit(&mut self, fork: &str) -> Result<Inherited, Failure> {
        identifier(fork)?;
        let record = self
            .refs
            .get(&branch_name(fork))
            .ok_or_else(|| Failure::new("branch_missing", fork))?;
        let head = Captured {
            branch: fork.to_owned(),
            version: version(record),
            shards: record.shards.clone(),
        };
        let companion = embedded::companion_name(fork);
        if self.refs.get(&companion).is_none() {
            let Some(origin) = provenance(&self.reader, &head)? else {
                return Ok(Inherited::Nothing);
            };
            let Some(parent) = self.refs.get(&embedded::companion_name(&origin.branch)) else {
                return Ok(Inherited::Nothing);
            };
            for ((collection, field), position) in self.companion_positions(parent)? {
                if position.toward.is_some() {
                    return Ok(Inherited::Waiting { collection, field });
                }
            }
            let roots: Vec<_> = parent
                .shards
                .iter()
                .map(|shard| (shard.shard_id, shard.head))
                .collect();
            create_branch_with_kind(
                &companion,
                roots,
                BranchKind::Namespace,
                &mut self.refs,
                &self.registry,
                current_timestamp(),
            )
            .map_err(Failure::branch_commit)?;
            self.publish_record(&companion)?;
            self.drop_undeclared(&head, &companion)?;
            return Ok(Inherited::Done);
        }
        if self.drop_undeclared(&head, &companion)? {
            return Ok(Inherited::Done);
        }
        Ok(Inherited::Already)
    }

    /// Removes, in one commit, the position of every field `head` does not
    /// declare embedded; whether there was any.
    fn drop_undeclared(&mut self, head: &Captured, companion: &str) -> Result<bool, Failure> {
        let record = self
            .refs
            .get(companion)
            .ok_or_else(|| Failure::new("reference_missing", companion))?;
        let mut removals = Vec::new();
        for ((collection, field), _position) in self.companion_positions(record)? {
            let key = super::embed_job::field_key(&collection, &field);
            let declared = match reads::get_bytes(&self.reader, head, &key)? {
                Some(bytes) => matches!(embedded::read_field(&bytes)?, Field::Embedded(_)),
                None => false,
            };
            if !declared {
                removals.push((embedded::position_key(&collection, &field), None));
            }
        }
        if removals.is_empty() {
            return Ok(false);
        }
        self.commit_namespace(companion, &removals)?;
        Ok(true)
    }
}

#[cfg(test)]
#[path = "embed_inherit_tests.rs"]
mod tests;