Skip to main content

mnemos/
error.rs

1//! Error types for the knowledge base.
2
3use thiserror::Error;
4
5/// Result type alias using the crate's Error type.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Errors that can occur in the knowledge base.
9#[derive(Error, Debug)]
10pub enum Error {
11    /// Entry not found in the knowledge base.
12    #[error("Entry not found: {0}")]
13    NotFound(String),
14
15    /// Invalid embedding dimension.
16    #[error("Invalid embedding dimension: expected {expected}, got {actual}")]
17    DimensionMismatch {
18        /// The dimension the operation required.
19        expected: usize,
20        /// The dimension that was actually provided.
21        actual: usize,
22    },
23
24    /// Storage backend error.
25    #[error("Storage error: {0}")]
26    Storage(String),
27
28    /// Embedding generation error.
29    #[error("Embedding error: {0}")]
30    Embedding(String),
31
32    /// Learning engine error.
33    #[error("Learning error: {0}")]
34    Learning(String),
35
36    /// Serialization error.
37    #[error("Serialization error: {0}")]
38    Serialization(#[from] bincode::Error),
39
40    /// JSON serialization error.
41    #[error("JSON error: {0}")]
42    Json(#[from] serde_json::Error),
43
44    /// IO error.
45    #[error("IO error: {0}")]
46    Io(#[from] std::io::Error),
47
48    /// Invalid configuration.
49    #[error("Invalid configuration: {0}")]
50    Config(String),
51
52    /// Index corruption detected.
53    #[error("Index corruption: {0}")]
54    IndexCorruption(String),
55
56    /// Concurrent access conflict.
57    #[error("Concurrent access conflict: {0}")]
58    ConcurrencyConflict(String),
59
60    /// Ingest/parsing error.
61    #[error("Ingest error: {0}")]
62    Ingest(String),
63}
64
65impl Error {
66    /// Create a storage error.
67    pub fn storage(msg: impl Into<String>) -> Self {
68        Self::Storage(msg.into())
69    }
70
71    /// Create an embedding error.
72    pub fn embedding(msg: impl Into<String>) -> Self {
73        Self::Embedding(msg.into())
74    }
75
76    /// Create a learning error.
77    pub fn learning(msg: impl Into<String>) -> Self {
78        Self::Learning(msg.into())
79    }
80
81    /// Create a not found error.
82    pub fn not_found(id: impl Into<String>) -> Self {
83        Self::NotFound(id.into())
84    }
85
86    /// Create a config error.
87    pub fn config(msg: impl Into<String>) -> Self {
88        Self::Config(msg.into())
89    }
90
91    /// Create an ingest error.
92    pub fn ingest(msg: impl Into<String>) -> Self {
93        Self::Ingest(msg.into())
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn constructors_and_display() {
103        let cases: Vec<(Error, &str)> = vec![
104            (Error::storage("disk full"), "Storage error: disk full"),
105            (
106                Error::embedding("bad provider"),
107                "Embedding error: bad provider",
108            ),
109            (Error::learning("nan"), "Learning error: nan"),
110            (Error::not_found("abc"), "Entry not found: abc"),
111            (
112                Error::config("missing key"),
113                "Invalid configuration: missing key",
114            ),
115            (Error::ingest("bad markdown"), "Ingest error: bad markdown"),
116            (
117                Error::DimensionMismatch {
118                    expected: 384,
119                    actual: 128,
120                },
121                "Invalid embedding dimension: expected 384, got 128",
122            ),
123            (
124                Error::IndexCorruption("checksum".into()),
125                "Index corruption: checksum",
126            ),
127            (
128                Error::ConcurrencyConflict("lock".into()),
129                "Concurrent access conflict: lock",
130            ),
131        ];
132        for (err, expected) in cases {
133            assert_eq!(err.to_string(), expected);
134        }
135    }
136
137    #[test]
138    fn from_io_and_json() {
139        let io: Error = std::io::Error::new(std::io::ErrorKind::NotFound, "x").into();
140        assert!(matches!(io, Error::Io(_)));
141        assert!(io.to_string().starts_with("IO error:"));
142
143        let json_err = serde_json::from_str::<u32>("not json").unwrap_err();
144        let json: Error = json_err.into();
145        assert!(matches!(json, Error::Json(_)));
146    }
147
148    #[test]
149    fn from_bincode() {
150        // bincode 1.x errors implement From; serializing an unsupported type
151        // (e.g. a Vec longer than its size limit) produces one. Easiest
152        // construction: deserialize garbage bytes into an i32.
153        let bin: Result<i32> = bincode::deserialize::<i32>(&[]).map_err(Into::into);
154        let err = bin.unwrap_err();
155        assert!(matches!(err, Error::Serialization(_)));
156        assert!(err.to_string().starts_with("Serialization error:"));
157    }
158}