1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Error, Debug)]
10pub enum Error {
11 #[error("Entry not found: {0}")]
13 NotFound(String),
14
15 #[error("Invalid embedding dimension: expected {expected}, got {actual}")]
17 DimensionMismatch {
18 expected: usize,
20 actual: usize,
22 },
23
24 #[error("Storage error: {0}")]
26 Storage(String),
27
28 #[error("Embedding error: {0}")]
30 Embedding(String),
31
32 #[error("Learning error: {0}")]
34 Learning(String),
35
36 #[error("Serialization error: {0}")]
38 Serialization(#[from] bincode::Error),
39
40 #[error("JSON error: {0}")]
42 Json(#[from] serde_json::Error),
43
44 #[error("IO error: {0}")]
46 Io(#[from] std::io::Error),
47
48 #[error("Invalid configuration: {0}")]
50 Config(String),
51
52 #[error("Index corruption: {0}")]
54 IndexCorruption(String),
55
56 #[error("Concurrent access conflict: {0}")]
58 ConcurrencyConflict(String),
59
60 #[error("Ingest error: {0}")]
62 Ingest(String),
63}
64
65impl Error {
66 pub fn storage(msg: impl Into<String>) -> Self {
68 Self::Storage(msg.into())
69 }
70
71 pub fn embedding(msg: impl Into<String>) -> Self {
73 Self::Embedding(msg.into())
74 }
75
76 pub fn learning(msg: impl Into<String>) -> Self {
78 Self::Learning(msg.into())
79 }
80
81 pub fn not_found(id: impl Into<String>) -> Self {
83 Self::NotFound(id.into())
84 }
85
86 pub fn config(msg: impl Into<String>) -> Self {
88 Self::Config(msg.into())
89 }
90
91 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 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}