haematite 0.5.0

Content-addressed, branchable, actor-native storage engine
Documentation
use std::fmt;

/// Durable role of a named branch.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BranchKind {
    /// A component namespace: a permanent isolation boundary that never merges
    /// away into another branch.
    Namespace,
    /// A mutable work branch. Pre-marker ref records decode to this permissive
    /// compatibility default.
    Work,
}

impl fmt::Display for BranchKind {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Namespace => formatter.write_str("Namespace"),
            Self::Work => formatter.write_str("Work"),
        }
    }
}

/// Typed engine refusals at namespace boundaries.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BranchPolicyError {
    /// A fork would change an existing Namespace lineage.
    ForkAcrossNamespaceBoundary {
        /// Durable source branch name.
        source: String,
        /// Durable source branch kind.
        source_kind: BranchKind,
        /// Namespace lineage resolved from the source ref record.
        source_lineage: Option<String>,
        /// Durable destination branch name.
        destination: String,
        /// Durable destination branch kind.
        destination_kind: BranchKind,
        /// Namespace lineage the destination record would resolve to.
        destination_lineage: Option<String>,
    },
    /// A merge would move content between different Namespace lineages.
    MergeAcrossNamespaceBoundary {
        /// Durable source branch name.
        source: String,
        /// Durable source branch kind.
        source_kind: BranchKind,
        /// Namespace lineage resolved from the source ref record.
        source_lineage: Option<String>,
        /// Durable destination branch name.
        destination: String,
        /// Durable destination branch kind.
        destination_kind: BranchKind,
        /// Namespace lineage resolved from the destination ref record.
        destination_lineage: Option<String>,
    },
    /// A Namespace branch was supplied as a merge source.
    NamespaceMerge {
        /// Durable source branch name.
        source: String,
        /// Durable source branch kind.
        source_kind: BranchKind,
        /// Durable destination branch name.
        destination: String,
        /// Durable destination branch kind.
        destination_kind: BranchKind,
    },
}

impl fmt::Display for BranchPolicyError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::ForkAcrossNamespaceBoundary {
                source,
                source_kind,
                source_lineage,
                destination,
                destination_kind,
                destination_lineage,
            } => write!(
                formatter,
                "policy refused fork: '{source}' ({source_kind}, lineage {}) into \
                 '{destination}' ({destination_kind}, lineage {})",
                Lineage(source_lineage.as_deref()),
                Lineage(destination_lineage.as_deref()),
            ),
            Self::MergeAcrossNamespaceBoundary {
                source,
                source_kind,
                source_lineage,
                destination,
                destination_kind,
                destination_lineage,
            } => write!(
                formatter,
                "policy refused merge: '{source}' ({source_kind}, lineage {}) into \
                 '{destination}' ({destination_kind}, lineage {})",
                Lineage(source_lineage.as_deref()),
                Lineage(destination_lineage.as_deref()),
            ),
            Self::NamespaceMerge {
                source,
                source_kind,
                destination,
                destination_kind,
            } => write!(
                formatter,
                "policy refused merge: '{source}' ({source_kind}) into \
                 '{destination}' ({destination_kind}); a Namespace branch never merges away",
            ),
        }
    }
}

impl std::error::Error for BranchPolicyError {}

struct Lineage<'a>(Option<&'a str>);

impl fmt::Display for Lineage<'_> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.0 {
            Some(lineage) => formatter.write_str(lineage),
            None => formatter.write_str("none"),
        }
    }
}