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>;