Skip to main content

soma_som_ring/
error.rs

1// SPDX-License-Identifier: LGPL-3.0-only
2#![allow(missing_docs)]
3
4//! Error types for the ring execution engine.
5//!
6//! [`RingEngineError`] is the engine's own error enum. Per-processor
7//! failures are captured as opaque strings via `ProcessorFailed` so the
8//! engine never names the concrete error type of any registered
9//! [`crate::RingProcessor`] implementation.
10
11use crate::boundary::BoundaryError;
12use soma_som_core::error::SomaError;
13use soma_som_core::extension::GateRejection;
14use soma_som_core::types::UnitId;
15
16/// Errors produced by ring engine operations.
17///
18/// Marked `#[non_exhaustive]` so adding a variant in a future minor release
19/// is not a breaking change. Consumers must include a wildcard arm in any
20/// exhaustive `match`.
21#[derive(Debug, thiserror::Error)]
22#[non_exhaustive]
23pub enum RingEngineError {
24    // ── Lifecycle ────────────────────────────────────────────────────────
25    /// Genesis has not been run yet.
26    #[error("Ring engine not initialized — run genesis first")]
27    NotInitialized,
28
29    /// Genesis has already been completed.
30    #[error("Genesis already completed — ring is in standard operation")]
31    GenesisAlreadyComplete,
32
33    // ── Processor failures ───────────────────────────────────────────────
34    /// A unit's processor returned an error.
35    ///
36    /// The reason is an opaque string because the engine does not know
37    /// the concrete error type of registered [`crate::RingProcessor`]
38    /// implementations.
39    #[error("Unit {unit} processing failed: {reason}")]
40    ProcessorFailed { unit: UnitId, reason: String },
41
42    /// A unit has no registered processor.
43    #[error("Unit {unit} is disabled — no processor registered")]
44    UnitDisabled { unit: UnitId },
45
46    // ── Extension errors ─────────────────────────────────────────────────
47    /// A BEFORE gate rejected the cycle.
48    #[error("Cycle rejected by BEFORE gate: {0}")]
49    GateRejected(#[from] GateRejection),
50
51    // ── Delegation ───────────────────────────────────────────────────────
52    /// A boundary operation failed.
53    #[error("Boundary error: {0}")]
54    Boundary(#[from] BoundaryError),
55
56    /// A core structural operation failed.
57    #[error("Core error: {0}")]
58    Core(#[from] SomaError),
59
60    // ── Cycle-level ──────────────────────────────────────────────────────
61    /// The ring cycle failed (aggregated reason).
62    #[error("Cycle {cycle_index} failed: {reason}")]
63    CycleFailed { cycle_index: u64, reason: String },
64}
65
66/// Result type alias for ring engine operations.
67pub type RingEngineResult<T> = Result<T, RingEngineError>;
68
69// inline: exercises module-private items via super::*
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn error_display_not_initialized() {
76        let e = RingEngineError::NotInitialized;
77        assert!(e.to_string().contains("not initialized"));
78    }
79
80    #[test]
81    fn error_display_processor_failed() {
82        let e = RingEngineError::ProcessorFailed {
83            unit: UnitId::FU,
84            reason: "timeout".into(),
85        };
86        assert!(e.to_string().contains("FU"));
87        assert!(e.to_string().contains("timeout"));
88    }
89
90    #[test]
91    fn error_display_unit_disabled() {
92        let e = RingEngineError::UnitDisabled { unit: UnitId::CU };
93        assert!(e.to_string().contains("CU"));
94        assert!(e.to_string().contains("disabled"));
95    }
96
97    #[test]
98    fn error_display_gate_rejected() {
99        let rejection = GateRejection {
100            source: "guard".into(),
101            reason: "rate limit".into(),
102        };
103        let e = RingEngineError::GateRejected(rejection);
104        assert!(e.to_string().contains("guard"));
105    }
106
107    #[test]
108    fn error_display_cycle_failed() {
109        let e = RingEngineError::CycleFailed {
110            cycle_index: 42,
111            reason: "integrity".into(),
112        };
113        assert!(e.to_string().contains("42"));
114    }
115}