1use thiserror::Error;
7
8#[derive(Debug, Error)]
13pub enum CvxError {
14 #[error(transparent)]
16 Storage(#[from] StorageError),
17
18 #[error(transparent)]
20 Index(#[from] IndexError),
21
22 #[error(transparent)]
24 Query(#[from] QueryError),
25
26 #[error(transparent)]
28 Ingest(#[from] IngestError),
29
30 #[error(transparent)]
32 Analytics(#[from] AnalyticsError),
33
34 #[error(transparent)]
36 Explain(#[from] ExplainError),
37
38 #[error("configuration error: {0}")]
40 Config(String),
41}
42
43pub type CvxResult<T> = Result<T, CvxError>;
45
46#[derive(Debug, Error)]
48pub enum StorageError {
49 #[error("entity {entity_id} not found in space {space_id}")]
51 EntityNotFound {
52 entity_id: u64,
54 space_id: u32,
56 },
57
58 #[error("dimension mismatch: expected {expected}, got {got}")]
60 DimensionMismatch {
61 expected: usize,
63 got: usize,
65 },
66
67 #[error("WAL corrupted at offset {offset}")]
69 WalCorrupted {
70 offset: u64,
72 },
73
74 #[error("tier migration failed: {reason}")]
76 TierMigration {
77 reason: String,
79 },
80
81 #[error(transparent)]
83 Io(#[from] std::io::Error),
84}
85
86#[derive(Debug, Error)]
88pub enum IndexError {
89 #[error("node {0} not found in graph")]
91 NodeNotFound(u32),
92
93 #[error("graph corrupted: {reason}")]
95 GraphCorrupted {
96 reason: String,
98 },
99
100 #[error("dimension mismatch: index expects {expected}, got {got}")]
102 DimensionMismatch {
103 expected: usize,
105 got: usize,
107 },
108
109 #[error("insert failed: {reason}")]
111 InsertFailed {
112 reason: String,
114 },
115}
116
117#[derive(Debug, Error)]
119pub enum QueryError {
120 #[error("entity {0} not found")]
122 EntityNotFound(u64),
123
124 #[error("insufficient data: need {needed} points, have {have}")]
126 InsufficientData {
127 needed: usize,
129 have: usize,
131 },
132
133 #[error("query timeout exceeded")]
135 Timeout,
136
137 #[error("planning failed: {reason}")]
139 PlanningFailed {
140 reason: String,
142 },
143}
144
145#[derive(Debug, Error)]
147pub enum IngestError {
148 #[error("validation failed: {reason}")]
150 ValidationFailed {
151 reason: String,
153 },
154
155 #[error("dimension mismatch for entity {entity_id}: expected {expected}, got {got}")]
157 DimensionMismatch {
158 entity_id: u64,
160 expected: usize,
162 got: usize,
164 },
165
166 #[error("WAL full, backpressure active")]
168 WalFull,
169
170 #[error("backpressure threshold exceeded")]
172 BackpressureExceeded,
173}
174
175#[derive(Debug, Error)]
177pub enum AnalyticsError {
178 #[error("solver diverged at step {step}")]
180 SolverDiverged {
181 step: usize,
183 },
184
185 #[error("insufficient data: need {needed} points, have {have}")]
187 InsufficientData {
188 needed: usize,
190 have: usize,
192 },
193
194 #[error("model not loaded: {name}")]
196 ModelNotLoaded {
197 name: String,
199 },
200}
201
202#[derive(Debug, Error)]
204pub enum ExplainError {
205 #[error("unsupported projection method: {0}")]
207 UnsupportedProjection(String),
208
209 #[error("insufficient data for explanation: {reason}")]
211 InsufficientData {
212 reason: String,
214 },
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn storage_error_converts_to_cvx_error() {
223 let err = StorageError::EntityNotFound {
224 entity_id: 42,
225 space_id: 0,
226 };
227 let cvx_err: CvxError = err.into();
228 assert!(matches!(cvx_err, CvxError::Storage(_)));
229 assert!(cvx_err.to_string().contains("entity 42"));
230 }
231
232 #[test]
233 fn index_error_converts_to_cvx_error() {
234 let err = IndexError::NodeNotFound(99);
235 let cvx_err: CvxError = err.into();
236 assert!(matches!(cvx_err, CvxError::Index(_)));
237 }
238
239 #[test]
240 fn query_error_displays_correctly() {
241 let err = QueryError::InsufficientData { needed: 5, have: 2 };
242 assert_eq!(err.to_string(), "insufficient data: need 5 points, have 2");
243 }
244
245 #[test]
246 fn all_error_types_are_send_sync() {
247 fn assert_send_sync<T: Send + Sync>() {}
248 assert_send_sync::<CvxError>();
249 assert_send_sync::<StorageError>();
250 assert_send_sync::<IndexError>();
251 assert_send_sync::<QueryError>();
252 assert_send_sync::<IngestError>();
253 assert_send_sync::<AnalyticsError>();
254 assert_send_sync::<ExplainError>();
255 }
256}