use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HnswConfigError {
InvalidDimension,
InvalidMParameter,
InvalidEfConstruction,
InvalidEfSearch,
InvalidMaxLayers,
}
impl fmt::Display for HnswConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HnswConfigError::InvalidDimension => {
write!(f, "Vector dimension must be greater than 0")
}
HnswConfigError::InvalidMParameter => {
write!(
f,
"M parameter (connections per node) must be greater than 0"
)
}
HnswConfigError::InvalidEfConstruction => {
write!(f, "ef_construction must be >= M parameter")
}
HnswConfigError::InvalidEfSearch => {
write!(f, "ef_search parameter must be greater than 0")
}
HnswConfigError::InvalidMaxLayers => {
write!(f, "Maximum number of layers must be greater than 0")
}
}
}
}
impl std::error::Error for HnswConfigError {}
#[derive(Debug, Clone, PartialEq)]
pub enum HnswIndexError {
VectorDimensionMismatch {
expected: usize,
actual: usize,
},
DuplicateVectorId(u64),
VectorNotFound(u64),
IndexNotInitialized,
IndexCorrupted(String),
CapacityExceeded,
InvalidSearchParameters,
NodeNotFound(u64),
InvalidNodeId(u64),
SelfConnection(u64),
}
#[derive(Debug, Clone, PartialEq)]
pub enum HnswStorageError {
InvalidDimension(usize),
DimensionMismatch { expected: usize, actual: usize },
InvalidVectorData,
VectorNotFound(u64),
BatchSizeMismatch,
BackendNotSupported,
VectorTooLarge { size: usize, max_size: usize },
StorageCapacityExceeded,
IoError(String),
DatabaseError(String),
}
impl fmt::Display for HnswStorageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HnswStorageError::InvalidDimension(dim) => {
write!(f, "Invalid vector dimension: {}", dim)
}
HnswStorageError::DimensionMismatch { expected, actual } => {
write!(
f,
"Vector dimension mismatch: expected {}, got {}",
expected, actual
)
}
HnswStorageError::InvalidVectorData => {
write!(f, "Vector data contains invalid values (NaN, Inf, etc.)")
}
HnswStorageError::VectorNotFound(id) => {
write!(f, "Vector ID {} not found in storage", id)
}
HnswStorageError::BatchSizeMismatch => {
write!(f, "Batch operation size mismatch")
}
HnswStorageError::BackendNotSupported => {
write!(f, "Storage backend not supported")
}
HnswStorageError::VectorTooLarge { size, max_size } => {
write!(
f,
"Vector size {} exceeds maximum allowed size {}",
size, max_size
)
}
HnswStorageError::StorageCapacityExceeded => {
write!(f, "Storage capacity exceeded")
}
HnswStorageError::IoError(msg) => {
write!(f, "I/O error: {}", msg)
}
HnswStorageError::DatabaseError(msg) => {
write!(f, "Database error: {}", msg)
}
}
}
}
impl std::error::Error for HnswStorageError {}
impl fmt::Display for HnswIndexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HnswIndexError::VectorDimensionMismatch { expected, actual } => {
write!(
f,
"Vector dimension mismatch: expected {}, got {}",
expected, actual
)
}
HnswIndexError::DuplicateVectorId(id) => {
write!(f, "Vector ID {} already exists in index", id)
}
HnswIndexError::VectorNotFound(id) => {
write!(f, "Vector ID {} not found in index", id)
}
HnswIndexError::IndexNotInitialized => {
write!(f, "Index not initialized")
}
HnswIndexError::IndexCorrupted(msg) => {
write!(f, "Index corrupted: {}", msg)
}
HnswIndexError::CapacityExceeded => {
write!(f, "Index capacity exceeded")
}
HnswIndexError::InvalidSearchParameters => {
write!(f, "Invalid search parameters")
}
HnswIndexError::NodeNotFound(id) => {
write!(f, "Node {} not found in layer", id)
}
HnswIndexError::InvalidNodeId(id) => {
write!(f, "Invalid node ID: {}", id)
}
HnswIndexError::SelfConnection(id) => {
write!(f, "Attempt to connect node {} to itself", id)
}
}
}
}
impl std::error::Error for HnswIndexError {}
#[derive(Debug, Clone, PartialEq)]
pub enum HnswMultiLayerError {
LayerMappingConflict {
global_id: u64,
layer_id: usize,
local_id: u64,
expected: u64,
},
InconsistentMapping {
global_id: u64,
layer_id: usize,
local_id: u64,
mapped_global: u64,
},
InconsistentLayerState {
layer_id: usize,
expected_nodes: usize,
actual_nodes: usize,
},
LayerMemoryExceeded {
layer: usize,
required: usize,
available: usize,
},
CrossLayerSearchFailed {
from_layer: usize,
to_layer: usize,
},
LevelDistributionFailure {
attempts: usize,
max_level: usize,
},
}
impl fmt::Display for HnswMultiLayerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HnswMultiLayerError::LayerMappingConflict {
global_id,
layer_id,
local_id,
expected,
} => {
write!(
f,
"Layer mapping conflict: global ID {} in layer {} assigned local ID {}, expected {}",
global_id, layer_id, local_id, expected
)
}
HnswMultiLayerError::InconsistentMapping {
global_id,
layer_id,
local_id,
mapped_global,
} => {
write!(
f,
"Inconsistent mapping: global ID {} → layer {} → local ID {}, but local {} → global ID {}",
global_id, layer_id, local_id, local_id, mapped_global
)
}
HnswMultiLayerError::InconsistentLayerState {
layer_id,
expected_nodes,
actual_nodes,
} => {
write!(
f,
"Inconsistent layer state: layer {} expects {} nodes but has {}",
layer_id, expected_nodes, actual_nodes
)
}
HnswMultiLayerError::LayerMemoryExceeded {
layer,
required,
available,
} => {
write!(
f,
"Layer {} memory limit exceeded: required {} bytes, available {} bytes",
layer, required, available
)
}
HnswMultiLayerError::CrossLayerSearchFailed {
from_layer,
to_layer,
} => {
write!(
f,
"Cross-layer search failed: from layer {} to layer {}",
from_layer, to_layer
)
}
HnswMultiLayerError::LevelDistributionFailure {
attempts,
max_level,
} => {
write!(
f,
"Level distribution failed after {} attempts, max level {}",
attempts, max_level
)
}
}
}
}
impl std::error::Error for HnswMultiLayerError {}
#[derive(Debug, Clone, PartialEq)]
pub enum HnswError {
Config(HnswConfigError),
Index(HnswIndexError),
Storage(HnswStorageError),
MultiLayer(HnswMultiLayerError),
}
impl fmt::Display for HnswError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HnswError::Config(err) => write!(f, "Configuration error: {}", err),
HnswError::Index(err) => write!(f, "Index error: {}", err),
HnswError::Storage(err) => write!(f, "Storage error: {}", err),
HnswError::MultiLayer(err) => write!(f, "Multi-layer error: {}", err),
}
}
}
impl std::error::Error for HnswError {}
impl From<HnswConfigError> for HnswError {
fn from(err: HnswConfigError) -> Self {
HnswError::Config(err)
}
}
impl From<HnswIndexError> for HnswError {
fn from(err: HnswIndexError) -> Self {
HnswError::Index(err)
}
}
impl From<HnswStorageError> for HnswError {
fn from(err: HnswStorageError) -> Self {
HnswError::Storage(err)
}
}
impl From<HnswMultiLayerError> for HnswError {
fn from(err: HnswMultiLayerError) -> Self {
HnswError::MultiLayer(err)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_error_display() {
assert_eq!(
HnswConfigError::InvalidDimension.to_string(),
"Vector dimension must be greater than 0"
);
assert_eq!(
HnswConfigError::InvalidMParameter.to_string(),
"M parameter (connections per node) must be greater than 0"
);
assert_eq!(
HnswConfigError::InvalidEfConstruction.to_string(),
"ef_construction must be >= M parameter"
);
assert_eq!(
HnswConfigError::InvalidEfSearch.to_string(),
"ef_search parameter must be greater than 0"
);
assert_eq!(
HnswConfigError::InvalidMaxLayers.to_string(),
"Maximum number of layers must be greater than 0"
);
}
#[test]
fn test_index_error_display() {
let dim_error = HnswIndexError::VectorDimensionMismatch {
expected: 768,
actual: 512,
};
assert_eq!(
dim_error.to_string(),
"Vector dimension mismatch: expected 768, got 512"
);
let dup_error = HnswIndexError::DuplicateVectorId(42);
assert_eq!(
dup_error.to_string(),
"Vector ID 42 already exists in index"
);
let not_found = HnswIndexError::VectorNotFound(99);
assert_eq!(not_found.to_string(), "Vector ID 99 not found in index");
assert_eq!(
HnswIndexError::IndexNotInitialized.to_string(),
"Index not initialized"
);
let corrupted = HnswIndexError::IndexCorrupted("layer data corrupted".to_string());
assert_eq!(
corrupted.to_string(),
"Index corrupted: layer data corrupted"
);
assert_eq!(
HnswIndexError::CapacityExceeded.to_string(),
"Index capacity exceeded"
);
assert_eq!(
HnswIndexError::InvalidSearchParameters.to_string(),
"Invalid search parameters"
);
}
#[test]
fn test_hnsw_error_display() {
let config_err = HnswError::Config(HnswConfigError::InvalidDimension);
assert!(config_err.to_string().contains("Configuration error"));
assert!(
config_err
.to_string()
.contains("Vector dimension must be greater than 0")
);
let index_err = HnswError::Index(HnswIndexError::VectorNotFound(1));
assert!(index_err.to_string().contains("Index error"));
assert!(index_err.to_string().contains("Vector ID 1 not found"));
}
#[test]
fn test_error_conversions() {
let config_err = HnswConfigError::InvalidMParameter;
let hnsw_err: HnswError = config_err.into();
assert!(matches!(
hnsw_err,
HnswError::Config(HnswConfigError::InvalidMParameter)
));
let index_err = HnswIndexError::DuplicateVectorId(123);
let hnsw_err: HnswError = index_err.into();
assert!(matches!(
hnsw_err,
HnswError::Index(HnswIndexError::DuplicateVectorId(123))
));
}
#[test]
fn test_error_equality() {
assert_eq!(
HnswConfigError::InvalidDimension,
HnswConfigError::InvalidDimension
);
assert_ne!(
HnswConfigError::InvalidDimension,
HnswConfigError::InvalidMParameter
);
let dim_error1 = HnswIndexError::VectorDimensionMismatch {
expected: 256,
actual: 128,
};
let dim_error2 = HnswIndexError::VectorDimensionMismatch {
expected: 256,
actual: 128,
};
assert_eq!(dim_error1, dim_error2);
assert_ne!(
HnswIndexError::DuplicateVectorId(1),
HnswIndexError::DuplicateVectorId(2)
);
}
#[test]
fn test_error_debug_format() {
let config_err = HnswConfigError::InvalidEfConstruction;
assert_eq!(format!("{:?}", config_err), "InvalidEfConstruction");
let index_err = HnswIndexError::VectorDimensionMismatch {
expected: 768,
actual: 384,
};
let debug_str = format!("{:?}", index_err);
assert!(debug_str.contains("VectorDimensionMismatch"));
assert!(debug_str.contains("768"));
assert!(debug_str.contains("384"));
}
}