1use std::io;
8use std::path::PathBuf;
9use thiserror::Error;
10
11pub type Result<T> = std::result::Result<T, VecStoreError>;
13
14#[derive(Error, Debug)]
16pub enum VecStoreError {
17 #[error("I/O error: {0}")]
19 Io(#[from] io::Error),
20
21 #[error("Serialization error: {0}")]
23 Serialization(String),
24
25 #[error("Invalid vector dimension: expected {expected}, got {actual}")]
27 DimensionMismatch { expected: usize, actual: usize },
28
29 #[error("Vector with id '{id}' not found")]
31 VectorNotFound { id: String },
32
33 #[error("Invalid filter expression: {0}")]
35 InvalidFilter(String),
36
37 #[error("Filter parse error at position {position}: {message}")]
39 FilterParse { position: usize, message: String },
40
41 #[error("Index not initialized or corrupted")]
43 IndexNotInitialized,
44
45 #[error("Invalid configuration: {0}")]
47 InvalidConfig(String),
48
49 #[error("Database corruption detected at {path:?}: {reason}")]
51 Corruption { path: PathBuf, reason: String },
52
53 #[error("Snapshot error: {0}")]
55 Snapshot(String),
56
57 #[error("Snapshot '{name}' not found")]
59 SnapshotNotFound { name: String },
60
61 #[error("HNSW index error: {0}")]
63 HnswError(String),
64
65 #[error("Product Quantization error: {0}")]
67 PqError(String),
68
69 #[error("Quantizer not trained - call train() first")]
71 QuantizerNotTrained,
72
73 #[error("Insufficient training data: need at least {required}, got {actual}")]
75 InsufficientTrainingData { required: usize, actual: usize },
76
77 #[error("Hybrid search error: {0}")]
79 HybridSearch(String),
80
81 #[error("Text not indexed for id '{id}' - call index_text() first")]
83 TextNotIndexed { id: String },
84
85 #[error("Query cannot be empty")]
87 EmptyQuery,
88
89 #[error("Invalid parameter '{param}': {reason}")]
91 InvalidParameter { param: String, reason: String },
92
93 #[error("Memory limit exceeded: {current} bytes (limit: {limit} bytes)")]
95 MemoryLimitExceeded { current: usize, limit: usize },
96
97 #[error("Concurrent access error: {0}")]
99 ConcurrentAccess(String),
100
101 #[error("Lock error: {0}")]
103 LockError(String),
104
105 #[cfg(feature = "embeddings")]
107 #[error("Embedding error: {0}")]
108 Embedding(String),
109
110 #[cfg(feature = "embeddings")]
112 #[error("ONNX Runtime error: {0}")]
113 OnnxRuntime(String),
114
115 #[cfg(feature = "embeddings")]
117 #[error("Tokenization error: {0}")]
118 Tokenization(String),
119
120 #[cfg(feature = "python")]
122 #[error("Python binding error: {0}")]
123 Python(String),
124
125 #[cfg(feature = "wasm")]
127 #[error("WASM error: {0}")]
128 Wasm(String),
129
130 #[error("Feature '{feature}' not enabled - compile with --features {feature}")]
132 FeatureNotEnabled { feature: String },
133
134 #[error("Error: {0}")]
136 Other(String),
137}
138
139impl From<bincode::Error> for VecStoreError {
142 fn from(err: bincode::Error) -> Self {
143 VecStoreError::Serialization(err.to_string())
144 }
145}
146
147impl From<serde_json::Error> for VecStoreError {
148 fn from(err: serde_json::Error) -> Self {
149 VecStoreError::Serialization(err.to_string())
150 }
151}
152
153#[cfg(feature = "embeddings")]
154impl From<ort::OrtError> for VecStoreError {
155 fn from(err: ort::OrtError) -> Self {
156 VecStoreError::OnnxRuntime(err.to_string())
157 }
158}
159
160#[cfg(feature = "embeddings")]
161impl From<tokenizers::Error> for VecStoreError {
162 fn from(err: tokenizers::Error) -> Self {
163 VecStoreError::Tokenization(err.to_string())
164 }
165}
166
167impl<T> From<std::sync::PoisonError<T>> for VecStoreError {
168 fn from(err: std::sync::PoisonError<T>) -> Self {
169 VecStoreError::LockError(err.to_string())
170 }
171}
172
173impl VecStoreError {
176 pub fn dimension_mismatch(expected: usize, actual: usize) -> Self {
178 VecStoreError::DimensionMismatch { expected, actual }
179 }
180
181 pub fn vector_not_found(id: impl Into<String>) -> Self {
183 VecStoreError::VectorNotFound { id: id.into() }
184 }
185
186 pub fn invalid_filter(msg: impl Into<String>) -> Self {
188 VecStoreError::InvalidFilter(msg.into())
189 }
190
191 pub fn filter_parse(position: usize, message: impl Into<String>) -> Self {
193 VecStoreError::FilterParse {
194 position,
195 message: message.into(),
196 }
197 }
198
199 pub fn invalid_config(msg: impl Into<String>) -> Self {
201 VecStoreError::InvalidConfig(msg.into())
202 }
203
204 pub fn corruption(path: impl Into<PathBuf>, reason: impl Into<String>) -> Self {
206 VecStoreError::Corruption {
207 path: path.into(),
208 reason: reason.into(),
209 }
210 }
211
212 pub fn snapshot(msg: impl Into<String>) -> Self {
214 VecStoreError::Snapshot(msg.into())
215 }
216
217 pub fn snapshot_not_found(name: impl Into<String>) -> Self {
219 VecStoreError::SnapshotNotFound { name: name.into() }
220 }
221
222 pub fn hnsw_error(msg: impl Into<String>) -> Self {
224 VecStoreError::HnswError(msg.into())
225 }
226
227 pub fn pq_error(msg: impl Into<String>) -> Self {
229 VecStoreError::PqError(msg.into())
230 }
231
232 pub fn insufficient_training_data(required: usize, actual: usize) -> Self {
234 VecStoreError::InsufficientTrainingData { required, actual }
235 }
236
237 pub fn hybrid_search(msg: impl Into<String>) -> Self {
239 VecStoreError::HybridSearch(msg.into())
240 }
241
242 pub fn text_not_indexed(id: impl Into<String>) -> Self {
244 VecStoreError::TextNotIndexed { id: id.into() }
245 }
246
247 pub fn invalid_parameter(param: impl Into<String>, reason: impl Into<String>) -> Self {
249 VecStoreError::InvalidParameter {
250 param: param.into(),
251 reason: reason.into(),
252 }
253 }
254
255 pub fn memory_limit_exceeded(current: usize, limit: usize) -> Self {
257 VecStoreError::MemoryLimitExceeded { current, limit }
258 }
259
260 pub fn concurrent_access(msg: impl Into<String>) -> Self {
262 VecStoreError::ConcurrentAccess(msg.into())
263 }
264
265 pub fn feature_not_enabled(feature: impl Into<String>) -> Self {
267 VecStoreError::FeatureNotEnabled {
268 feature: feature.into(),
269 }
270 }
271
272 pub fn other(msg: impl Into<String>) -> Self {
274 VecStoreError::Other(msg.into())
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn test_dimension_mismatch() {
284 let err = VecStoreError::dimension_mismatch(128, 256);
285 assert_eq!(
286 err.to_string(),
287 "Invalid vector dimension: expected 128, got 256"
288 );
289 }
290
291 #[test]
292 fn test_vector_not_found() {
293 let err = VecStoreError::vector_not_found("vec_123");
294 assert_eq!(err.to_string(), "Vector with id 'vec_123' not found");
295 }
296
297 #[test]
298 fn test_filter_parse() {
299 let err = VecStoreError::filter_parse(42, "unexpected token");
300 assert_eq!(
301 err.to_string(),
302 "Filter parse error at position 42: unexpected token"
303 );
304 }
305
306 #[test]
307 fn test_memory_limit_exceeded() {
308 let err = VecStoreError::memory_limit_exceeded(1000000, 500000);
309 assert_eq!(
310 err.to_string(),
311 "Memory limit exceeded: 1000000 bytes (limit: 500000 bytes)"
312 );
313 }
314
315 #[test]
316 fn test_feature_not_enabled() {
317 let err = VecStoreError::feature_not_enabled("embeddings");
318 assert_eq!(
319 err.to_string(),
320 "Feature 'embeddings' not enabled - compile with --features embeddings"
321 );
322 }
323}