use std::io;
use std::path::PathBuf;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, VecStoreError>;
#[derive(Error, Debug)]
pub enum VecStoreError {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Invalid vector dimension: expected {expected}, got {actual}")]
DimensionMismatch { expected: usize, actual: usize },
#[error("Vector with id '{id}' not found")]
VectorNotFound { id: String },
#[error("Invalid filter expression: {0}")]
InvalidFilter(String),
#[error("Filter parse error at position {position}: {message}")]
FilterParse { position: usize, message: String },
#[error("Index not initialized or corrupted")]
IndexNotInitialized,
#[error("Invalid configuration: {0}")]
InvalidConfig(String),
#[error("Database corruption detected at {path:?}: {reason}")]
Corruption { path: PathBuf, reason: String },
#[error("Snapshot error: {0}")]
Snapshot(String),
#[error("Snapshot '{name}' not found")]
SnapshotNotFound { name: String },
#[error("HNSW index error: {0}")]
HnswError(String),
#[error("Product Quantization error: {0}")]
PqError(String),
#[error("Quantizer not trained - call train() first")]
QuantizerNotTrained,
#[error("Insufficient training data: need at least {required}, got {actual}")]
InsufficientTrainingData { required: usize, actual: usize },
#[error("Hybrid search error: {0}")]
HybridSearch(String),
#[error("Text not indexed for id '{id}' - call index_text() first")]
TextNotIndexed { id: String },
#[error("Query cannot be empty")]
EmptyQuery,
#[error("Invalid parameter '{param}': {reason}")]
InvalidParameter { param: String, reason: String },
#[error("Memory limit exceeded: {current} bytes (limit: {limit} bytes)")]
MemoryLimitExceeded { current: usize, limit: usize },
#[error("Concurrent access error: {0}")]
ConcurrentAccess(String),
#[error("Lock error: {0}")]
LockError(String),
#[cfg(feature = "embeddings")]
#[error("Embedding error: {0}")]
Embedding(String),
#[cfg(feature = "embeddings")]
#[error("ONNX Runtime error: {0}")]
OnnxRuntime(String),
#[cfg(feature = "embeddings")]
#[error("Tokenization error: {0}")]
Tokenization(String),
#[cfg(feature = "python")]
#[error("Python binding error: {0}")]
Python(String),
#[cfg(feature = "wasm")]
#[error("WASM error: {0}")]
Wasm(String),
#[error("Feature '{feature}' not enabled - compile with --features {feature}")]
FeatureNotEnabled { feature: String },
#[error("Error: {0}")]
Other(String),
}
impl From<bincode::Error> for VecStoreError {
fn from(err: bincode::Error) -> Self {
VecStoreError::Serialization(err.to_string())
}
}
impl From<serde_json::Error> for VecStoreError {
fn from(err: serde_json::Error) -> Self {
VecStoreError::Serialization(err.to_string())
}
}
#[cfg(feature = "embeddings")]
impl From<ort::OrtError> for VecStoreError {
fn from(err: ort::OrtError) -> Self {
VecStoreError::OnnxRuntime(err.to_string())
}
}
#[cfg(feature = "embeddings")]
impl From<tokenizers::Error> for VecStoreError {
fn from(err: tokenizers::Error) -> Self {
VecStoreError::Tokenization(err.to_string())
}
}
impl<T> From<std::sync::PoisonError<T>> for VecStoreError {
fn from(err: std::sync::PoisonError<T>) -> Self {
VecStoreError::LockError(err.to_string())
}
}
impl VecStoreError {
pub fn dimension_mismatch(expected: usize, actual: usize) -> Self {
VecStoreError::DimensionMismatch { expected, actual }
}
pub fn vector_not_found(id: impl Into<String>) -> Self {
VecStoreError::VectorNotFound { id: id.into() }
}
pub fn invalid_filter(msg: impl Into<String>) -> Self {
VecStoreError::InvalidFilter(msg.into())
}
pub fn filter_parse(position: usize, message: impl Into<String>) -> Self {
VecStoreError::FilterParse {
position,
message: message.into(),
}
}
pub fn invalid_config(msg: impl Into<String>) -> Self {
VecStoreError::InvalidConfig(msg.into())
}
pub fn corruption(path: impl Into<PathBuf>, reason: impl Into<String>) -> Self {
VecStoreError::Corruption {
path: path.into(),
reason: reason.into(),
}
}
pub fn snapshot(msg: impl Into<String>) -> Self {
VecStoreError::Snapshot(msg.into())
}
pub fn snapshot_not_found(name: impl Into<String>) -> Self {
VecStoreError::SnapshotNotFound { name: name.into() }
}
pub fn hnsw_error(msg: impl Into<String>) -> Self {
VecStoreError::HnswError(msg.into())
}
pub fn pq_error(msg: impl Into<String>) -> Self {
VecStoreError::PqError(msg.into())
}
pub fn insufficient_training_data(required: usize, actual: usize) -> Self {
VecStoreError::InsufficientTrainingData { required, actual }
}
pub fn hybrid_search(msg: impl Into<String>) -> Self {
VecStoreError::HybridSearch(msg.into())
}
pub fn text_not_indexed(id: impl Into<String>) -> Self {
VecStoreError::TextNotIndexed { id: id.into() }
}
pub fn invalid_parameter(param: impl Into<String>, reason: impl Into<String>) -> Self {
VecStoreError::InvalidParameter {
param: param.into(),
reason: reason.into(),
}
}
pub fn memory_limit_exceeded(current: usize, limit: usize) -> Self {
VecStoreError::MemoryLimitExceeded { current, limit }
}
pub fn concurrent_access(msg: impl Into<String>) -> Self {
VecStoreError::ConcurrentAccess(msg.into())
}
pub fn feature_not_enabled(feature: impl Into<String>) -> Self {
VecStoreError::FeatureNotEnabled {
feature: feature.into(),
}
}
pub fn other(msg: impl Into<String>) -> Self {
VecStoreError::Other(msg.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dimension_mismatch() {
let err = VecStoreError::dimension_mismatch(128, 256);
assert_eq!(
err.to_string(),
"Invalid vector dimension: expected 128, got 256"
);
}
#[test]
fn test_vector_not_found() {
let err = VecStoreError::vector_not_found("vec_123");
assert_eq!(err.to_string(), "Vector with id 'vec_123' not found");
}
#[test]
fn test_filter_parse() {
let err = VecStoreError::filter_parse(42, "unexpected token");
assert_eq!(
err.to_string(),
"Filter parse error at position 42: unexpected token"
);
}
#[test]
fn test_memory_limit_exceeded() {
let err = VecStoreError::memory_limit_exceeded(1000000, 500000);
assert_eq!(
err.to_string(),
"Memory limit exceeded: 1000000 bytes (limit: 500000 bytes)"
);
}
#[test]
fn test_feature_not_enabled() {
let err = VecStoreError::feature_not_enabled("embeddings");
assert_eq!(
err.to_string(),
"Feature 'embeddings' not enabled - compile with --features embeddings"
);
}
}