Skip to main content

dsfb_debug/
error.rs

1//! DSFB-Debug: error types — additive, Copy, no-panic.
2//!
3//! Every fallible operation in the crate returns `Result<T, DsfbError>`
4//! rather than panicking. The crate's top-level lints
5//! (`#![deny(clippy::unwrap_used)]`) enforce this at compile time.
6//!
7//! `DsfbError` is `Copy + Clone + Debug + PartialEq + Eq` — every
8//! variant is a stack-only enum, no heap allocation. Variants are
9//! additive across versions: existing variants are never removed or
10//! renumbered, so cross-version `match` arms remain valid.
11//!
12//! # Variant taxonomy
13//!
14//! - **DimensionMismatch / BufferTooSmall** — caller-shape errors
15//!   (operator passed wrong slice lengths or undersized output buffer)
16//! - **MissingRealData / HashMismatch** — `paper-lock` integrity
17//!   gates (sentinel fixture / SHA-256 drift)
18//! - **ParseError** — adapter/fixture-format errors (invalid TSV,
19//!   missing header, etc.)
20//! - **InvalidConfig** — paper-lock config-construction errors
21//!
22//! Operators reading these errors at runtime can map each variant to
23//! a specific corrective action; the crate documents the action in
24//! each variant's per-variant doc-comment.
25
26/// All errors the DSFB engine can produce.
27/// No heap allocation — all variants are Copy.
28#[derive(Copy, Clone, Debug, PartialEq, Eq)]
29pub enum DsfbError {
30    /// Input slice length does not match expected signal count
31    DimensionMismatch { expected: usize, got: usize },
32    /// Baseline has not been established (healthy window not computed)
33    BaselineNotEstablished,
34    /// Envelope radius is zero or negative for a signal
35    InvalidEnvelopeRadius { signal_index: usize },
36    /// Window index is out of range for history buffer
37    WindowOutOfRange { index: u64 },
38    /// Episode buffer is full — increase MAX_EPISODES
39    EpisodeBufferFull,
40    /// Signal buffer is full — increase MAX_SIGNALS
41    SignalBufferFull,
42    /// History buffer is full — increase MAX_WINDOWS
43    HistoryBufferFull,
44    /// Heuristics bank is full — increase MAX_MOTIFS
45    HeuristicsBankFull,
46    /// Configuration parameter is invalid
47    InvalidConfig(&'static str),
48    /// Dataset parse error at a specific record
49    ParseError { record: u64, field: u16 },
50    /// Insufficient healthy-window data for baseline
51    InsufficientBaselineData { available: usize, required: usize },
52    /// Internal flat-aggregation buffer too small for the requested
53    /// `num_signals * num_windows`. Returned at `run_evaluation` entry.
54    BufferTooSmall { needed: usize, available: usize },
55    /// `paper-lock` real-data evaluation requested but the supplied fixture
56    /// bytes are empty. Never falls back to synthetic data.
57    MissingRealData,
58    /// SHA-256 of supplied fixture bytes does not match the manifest's
59    /// `fixture_sha256`. Indicates tampering or upstream drift; the
60    /// engine refuses to proceed.
61    HashMismatch,
62}
63
64impl core::fmt::Display for DsfbError {
65    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66        match self {
67            Self::DimensionMismatch { expected, got } => {
68                write!(f, "dimension mismatch: expected {}, got {}", expected, got)
69            }
70            Self::BaselineNotEstablished => write!(f, "baseline not established"),
71            Self::InvalidEnvelopeRadius { signal_index } => {
72                write!(f, "invalid envelope radius at signal {}", signal_index)
73            }
74            Self::WindowOutOfRange { index } => {
75                write!(f, "window index {} out of range", index)
76            }
77            Self::EpisodeBufferFull => write!(f, "episode buffer full"),
78            Self::SignalBufferFull => write!(f, "signal buffer full"),
79            Self::HistoryBufferFull => write!(f, "history buffer full"),
80            Self::HeuristicsBankFull => write!(f, "heuristics bank full"),
81            Self::InvalidConfig(msg) => write!(f, "invalid config: {}", msg),
82            Self::ParseError { record, field } => {
83                write!(f, "parse error at record {}, field {}", record, field)
84            }
85            Self::InsufficientBaselineData { available, required } => {
86                write!(f, "insufficient baseline data: {} of {} required", available, required)
87            }
88            Self::BufferTooSmall { needed, available } => {
89                write!(f, "internal flat buffer too small: needed {}, available {}", needed, available)
90            }
91            Self::MissingRealData => write!(f, "real-data fixture missing or empty (paper-lock refuses to fall back to synthetic)"),
92            Self::HashMismatch => write!(f, "fixture SHA-256 does not match manifest"),
93        }
94    }
95}
96
97pub type Result<T> = core::result::Result<T, DsfbError>;