Skip to main content

synaptic_wiring/
error.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Error types for `synaptic-wiring`.
4//!
5//! Follows the `corinth-canal` pattern of a single unified error enum
6//! using `thiserror` for ergonomic `Display` and `From` implementations.
7
8use std::fmt;
9
10/// Unified error type for synaptic-wiring operations.
11#[derive(Debug, Clone, PartialEq)]
12pub enum MeshError {
13    /// A required parameter was out of range or invalid.
14    InvalidConfig(String),
15
16    /// [`crate::RouterConfig`] or a [`crate::ChannelRouter`] checkpoint failed
17    /// validation.
18    ///
19    /// `field` is the config or state field that failed (`"leak"`,
20    /// `"neurons"`, `"baseline_weights"`, …) so constructor and serde paths
21    /// can be compared by category without parsing the reason string.
22    InvalidRouterConfig {
23        /// Config or checkpoint field that failed validation.
24        field: &'static str,
25        /// Why the value is rejected.
26        reason: String,
27    },
28
29    /// Neuron count mismatch between components.
30    NeuronCountMismatch {
31        expected: usize,
32        got: usize,
33        context: String,
34    },
35
36    /// Attempted to access a neuron or synapse that doesn't exist.
37    IndexOutOfBounds { index: usize, max: usize },
38
39    /// Topology generation failed (e.g., impossible wiring constraints).
40    TopologyError(String),
41
42    /// Delay buffer overflow or misconfiguration.
43    DelayError(String),
44
45    /// A [`ChannelRouter`](crate::router::ChannelRouter) channel signal was
46    /// NaN or ±infinity. `index` is the channel of the rejected sample;
47    /// `context` names the public entry point (`"route signals"` or
48    /// `"route_modulated signals"`).
49    NonFiniteSignal { index: usize, context: String },
50
51    /// A [`NeuromodState`](crate::router::NeuromodState) field was NaN or
52    /// ±infinity. `field` is `"cortisol"`, `"dopamine"`, or `"serotonin"`.
53    NonFiniteNeuromodulator { field: &'static str },
54
55    /// A [`NeuromodState`](crate::router::NeuromodState) field was finite but
56    /// outside the documented `[0, 1]` interval. Out-of-range values are
57    /// rejected rather than silently clamped.
58    OutOfRangeNeuromodulator { field: &'static str, value: f32 },
59}
60
61impl MeshError {
62    pub(crate) fn invalid_router_config(field: &'static str, reason: impl Into<String>) -> Self {
63        Self::InvalidRouterConfig {
64            field,
65            reason: reason.into(),
66        }
67    }
68}
69
70impl fmt::Display for MeshError {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self {
73            MeshError::InvalidConfig(msg) => write!(f, "invalid configuration: {msg}"),
74            MeshError::InvalidRouterConfig { field, reason } => {
75                write!(f, "invalid router config ({field}): {reason}")
76            }
77            MeshError::NeuronCountMismatch {
78                expected,
79                got,
80                context,
81            } => write!(
82                f,
83                "neuron count mismatch in {context}: expected {expected}, got {got}"
84            ),
85            MeshError::IndexOutOfBounds { index, max } => {
86                write!(f, "index {index} out of bounds (max {max})")
87            }
88            MeshError::TopologyError(msg) => write!(f, "topology error: {msg}"),
89            MeshError::DelayError(msg) => write!(f, "delay error: {msg}"),
90            MeshError::NonFiniteSignal { index, context } => {
91                write!(f, "non-finite signal in {context}[{index}]")
92            }
93            MeshError::NonFiniteNeuromodulator { field } => {
94                write!(f, "non-finite neuromodulator field {field}")
95            }
96            MeshError::OutOfRangeNeuromodulator { field, value } => {
97                write!(
98                    f,
99                    "neuromodulator field {field} must be in [0, 1], got {value}"
100                )
101            }
102        }
103    }
104}
105
106impl std::error::Error for MeshError {}
107
108/// Convenience alias used throughout the crate.
109pub type Result<T> = std::result::Result<T, MeshError>;