use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("configuration error: {0}")]
Configuration(String),
#[error("connection error: {0}")]
Connection(String),
#[error("adapter is not connected")]
NotConnected,
#[error("operation failed: {0}")]
Operation(String),
#[error("not found: {0}")]
NotFound(String),
#[error("namespace not found: {0}")]
NamespaceNotFound(String),
#[error("dimension mismatch: expected {expected}, got {actual}")]
DimensionMismatch { expected: usize, actual: usize },
#[error("missing dependency: {0}")]
MissingDependency(String),
#[error("backend error: {0}")]
Backend(String),
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
impl Error {
pub fn config(msg: impl Into<String>) -> Self {
Self::Configuration(msg.into())
}
pub fn connection(msg: impl Into<String>) -> Self {
Self::Connection(msg.into())
}
pub fn operation(msg: impl Into<String>) -> Self {
Self::Operation(msg.into())
}
pub fn backend(msg: impl Into<String>) -> Self {
Self::Backend(msg.into())
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn configuration_error_message() {
let e = Error::config("url is required");
assert_eq!(e.to_string(), "configuration error: url is required");
}
#[test]
fn connection_error_message() {
let e = Error::connection("timed out after 10s");
assert_eq!(e.to_string(), "connection error: timed out after 10s");
}
#[test]
fn not_connected_message() {
let e = Error::NotConnected;
assert_eq!(e.to_string(), "adapter is not connected");
}
#[test]
fn operation_error_message() {
let e = Error::operation("insert failed");
assert_eq!(e.to_string(), "operation failed: insert failed");
}
#[test]
fn not_found_message() {
let e = Error::NotFound("record-42".to_string());
assert_eq!(e.to_string(), "not found: record-42");
}
#[test]
fn namespace_not_found_message() {
let e = Error::NamespaceNotFound("documents".to_string());
assert_eq!(e.to_string(), "namespace not found: documents");
}
#[test]
fn dimension_mismatch_message() {
let e = Error::DimensionMismatch { expected: 384, actual: 512 };
assert_eq!(e.to_string(), "dimension mismatch: expected 384, got 512");
}
#[test]
fn missing_dependency_message() {
let e = Error::MissingDependency("pgvector extension".to_string());
assert_eq!(e.to_string(), "missing dependency: pgvector extension");
}
#[test]
fn backend_error_message() {
let e = Error::backend("driver panicked");
assert_eq!(e.to_string(), "backend error: driver panicked");
}
#[test]
fn config_accepts_str_slice() {
let e = Error::config("bad value");
assert!(matches!(e, Error::Configuration(_)));
}
#[test]
fn config_accepts_owned_string() {
let e = Error::config(String::from("bad value"));
assert!(matches!(e, Error::Configuration(_)));
}
#[test]
fn connection_constructor() {
let e = Error::connection("refused");
assert!(matches!(e, Error::Connection(_)));
}
#[test]
fn operation_constructor() {
let e = Error::operation("deadlock");
assert!(matches!(e, Error::Operation(_)));
}
#[test]
fn backend_constructor() {
let e = Error::backend("io error");
assert!(matches!(e, Error::Backend(_)));
}
#[test]
fn result_ok_variant() {
let r: Result<i32> = Ok(42);
assert_eq!(r.unwrap(), 42);
}
#[test]
fn result_err_variant() {
let r: Result<i32> = Err(Error::NotConnected);
assert!(r.is_err());
}
#[test]
fn other_via_from_boxed_error() {
let inner: Box<dyn std::error::Error + Send + Sync> =
Box::new(std::io::Error::new(std::io::ErrorKind::BrokenPipe, "pipe broke"));
let e = Error::from(inner);
assert!(matches!(e, Error::Other(_)));
assert!(e.to_string().contains("pipe broke"));
}
}