use std::path::PathBuf;
use thiserror::Error;
use super::entities::{ConfigValidationError, EmbeddingId};
#[derive(Debug, Error)]
pub enum VectorError {
#[error("Dimension mismatch: expected {expected}, got {got}")]
DimensionMismatch {
expected: usize,
got: usize,
},
#[error("Vector with ID {0} already exists")]
DuplicateId(EmbeddingId),
#[error("Vector with ID {0} not found")]
NotFound(EmbeddingId),
#[error("Index capacity exceeded: max {max}, current {current}")]
CapacityExceeded {
max: usize,
current: usize,
},
#[error("Invalid vector data: {0}")]
InvalidVector(String),
#[error("Configuration error: {0}")]
ConfigError(#[from] ConfigValidationError),
#[error("Index is empty")]
EmptyIndex,
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("File not found: {0}")]
FileNotFound(PathBuf),
#[error("Corrupted index file: {0}")]
CorruptedFile(String),
#[error("Failed to acquire lock: {0}")]
LockError(String),
#[error("Operation timed out after {0}ms")]
Timeout(u64),
#[error("Index not initialized")]
NotInitialized,
#[error("Concurrent modification detected")]
ConcurrentModification,
#[error("Graph error: {0}")]
GraphError(String),
#[error("Invalid search parameters: {0}")]
InvalidSearchParams(String),
#[error("Internal error: {0}")]
Internal(String),
}
impl VectorError {
pub fn dimension_mismatch(expected: usize, got: usize) -> Self {
Self::DimensionMismatch { expected, got }
}
pub fn capacity_exceeded(max: usize, current: usize) -> Self {
Self::CapacityExceeded { max, current }
}
pub fn invalid_vector(msg: impl Into<String>) -> Self {
Self::InvalidVector(msg.into())
}
pub fn serialization(msg: impl Into<String>) -> Self {
Self::SerializationError(msg.into())
}
pub fn corrupted(msg: impl Into<String>) -> Self {
Self::CorruptedFile(msg.into())
}
pub fn lock(msg: impl Into<String>) -> Self {
Self::LockError(msg.into())
}
pub fn graph(msg: impl Into<String>) -> Self {
Self::GraphError(msg.into())
}
pub fn invalid_search(msg: impl Into<String>) -> Self {
Self::InvalidSearchParams(msg.into())
}
pub fn internal(msg: impl Into<String>) -> Self {
Self::Internal(msg.into())
}
pub fn is_retriable(&self) -> bool {
matches!(
self,
Self::LockError(_) | Self::Timeout(_) | Self::ConcurrentModification
)
}
pub fn is_not_found(&self) -> bool {
matches!(self, Self::NotFound(_) | Self::FileNotFound(_))
}
}
impl From<bincode::Error> for VectorError {
fn from(e: bincode::Error) -> Self {
Self::SerializationError(e.to_string())
}
}
impl From<serde_json::Error> for VectorError {
fn from(e: serde_json::Error) -> Self {
Self::SerializationError(e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_messages() {
let err = VectorError::dimension_mismatch(1536, 768);
assert!(err.to_string().contains("1536"));
assert!(err.to_string().contains("768"));
let err = VectorError::NotFound(EmbeddingId::new());
assert!(err.to_string().contains("not found"));
}
#[test]
fn test_is_retriable() {
assert!(VectorError::lock("test").is_retriable());
assert!(VectorError::Timeout(1000).is_retriable());
assert!(!VectorError::EmptyIndex.is_retriable());
}
#[test]
fn test_is_not_found() {
assert!(VectorError::NotFound(EmbeddingId::new()).is_not_found());
assert!(VectorError::FileNotFound(PathBuf::from("/test")).is_not_found());
assert!(!VectorError::EmptyIndex.is_not_found());
}
}