soma-som-core 0.1.0

Universal soma(som) structural primitives — Quad / Tree / Ring / Genesis / Fingerprint / TemporalLedger / CrossingRecord
Documentation
// SPDX-License-Identifier: LGPL-3.0-only
#![allow(missing_docs)]

//! Error types for soma-som-core.
//!
//! All errors are structural: they represent violations of the specification's
//! invariants, protocol constraints, or lifecycle rules.
//!
//! **Spec:** OPUS §11 (Falsifiability — error taxonomy for ring integrity
//! violations).

use crate::types::UnitId;

// ── Shared redb error conversion macro ──────────────────────────────────────
//
// Five solitaires convert redb errors identically: stringify into a Store/Storage
// variant. This macro eliminates 25+ identical `From` impls across the workspace.
//
// Usage (at call site where `redb` is in scope):
//   soma_som_core::impl_redb_store_errors!(MyError, Store);           // 5 base types
//   soma_som_core::impl_redb_store_errors!(MyError, Store, +toplevel); // + redb::Error

/// Generate `From<redb::*Error>` impls that stringify into a single variant.
#[macro_export]
macro_rules! impl_redb_store_errors {
    ($err_type:ty, $variant:ident) => {
        impl From<redb::DatabaseError> for $err_type {
            fn from(e: redb::DatabaseError) -> Self { Self::$variant(e.to_string()) }
        }
        impl From<redb::TableError> for $err_type {
            fn from(e: redb::TableError) -> Self { Self::$variant(e.to_string()) }
        }
        impl From<redb::StorageError> for $err_type {
            fn from(e: redb::StorageError) -> Self { Self::$variant(e.to_string()) }
        }
        impl From<redb::TransactionError> for $err_type {
            fn from(e: redb::TransactionError) -> Self { Self::$variant(e.to_string()) }
        }
        impl From<redb::CommitError> for $err_type {
            fn from(e: redb::CommitError) -> Self { Self::$variant(e.to_string()) }
        }
    };
    ($err_type:ty, $variant:ident, +toplevel) => {
        impl From<redb::Error> for $err_type {
            fn from(e: redb::Error) -> Self { Self::$variant(e.to_string()) }
        }
        $crate::impl_redb_store_errors!($err_type, $variant);
    };
}

/// Errors produced by `soma-som-core` operations.
///
/// Marked `#[non_exhaustive]` so adding a variant in a future minor release
/// is not a breaking change. Consumers must include a wildcard arm in any
/// exhaustive `match`.
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
pub enum SomaError {
    // ── Ring topology errors (Invariants 2, 5, 7) ──────────────────────────
    /// Attempted transition violates ring order (Invariant 5: no bypass).
    #[error("Invalid transition: {from_unit} → {to_unit} (expected {from_unit} → {expected})")]
    InvalidTransition {
        from_unit: UnitId,
        to_unit: UnitId,
        expected: UnitId,
    },

    /// Ring has not been initialized (no genesis).
    #[error("Ring is not initialized — genesis has not occurred")]
    RingNotInitialized,

    // ── Genesis protocol errors (Spec §10) ─────────────────────────────────
    /// Genesis has already started; cannot reinitialize.
    #[error("Genesis has already started — ring is no longer uninitialized")]
    GenesisAlreadyStarted,

    /// Genesis pipeline is not in progress.
    #[error("Genesis is not in progress — cannot close ring")]
    GenesisNotInProgress,

    /// Genesis pipeline has already completed all units.
    #[error("Genesis pipeline is already complete — all units processed")]
    GenesisAlreadyComplete,

    /// Unit was completed out of pipeline order.
    #[error("Genesis out of order: expected {expected}, received {received}")]
    GenesisOutOfOrder { expected: UnitId, received: UnitId },

    /// Genesis pipeline has not yet completed all units.
    #[error("Genesis pipeline incomplete — not all units have been processed")]
    GenesisPipelineIncomplete,

    // ── Temporal ledger errors (Spec §9.5) ─────────────────────────────────
    /// Genesis entry has already been recorded.
    #[error("Genesis already recorded in the temporal ledger")]
    GenesisAlreadyRecorded,

    /// Attempted to record a cycle on an empty ledger (no genesis).
    #[error("Temporal ledger is empty — record genesis first")]
    LedgerEmpty,

    /// Chain integrity violation detected during verification.
    #[error("Chain integrity violation at cycle {cycle_index}")]
    ChainIntegrityViolation { cycle_index: u64 },

    // ── Envelope / boundary errors (Contracts §2–4) ────────────────────────
    /// Envelope failed structural validation.
    #[error("Envelope validation failed: {reason}")]
    EnvelopeInvalid { reason: String },

    /// Bounded completion time exceeded (Criterion 3).
    #[error("Cycle timeout: {elapsed_ms}ms exceeds Δt_max={max_ms}ms")]
    CycleTimeout { elapsed_ms: u64, max_ms: u64 },
}