1use prax_query::QueryError;
4use thiserror::Error;
5
6pub type VectorResult<T> = Result<T, VectorError>;
8
9#[derive(Error, Debug)]
11pub enum VectorError {
12 #[error("pgvector extension not installed: run CREATE EXTENSION vector")]
14 ExtensionNotInstalled,
15
16 #[error("dimension mismatch: expected {expected}, got {actual}")]
18 DimensionMismatch {
19 expected: usize,
21 actual: usize,
23 },
24
25 #[error("empty vector: vectors must have at least one dimension")]
27 EmptyVector,
28
29 #[error("invalid dimensions: {0}")]
31 InvalidDimensions(String),
32
33 #[error("index error: {0}")]
35 Index(String),
36
37 #[error("postgres error: {0}")]
39 Postgres(#[from] prax_postgres::PgError),
40
41 #[error("query error: {0}")]
43 Query(String),
44
45 #[error("type conversion error: {0}")]
47 TypeConversion(String),
48
49 #[error("configuration error: {0}")]
51 Config(String),
52}
53
54impl VectorError {
55 pub fn dimension_mismatch(expected: usize, actual: usize) -> Self {
57 Self::DimensionMismatch { expected, actual }
58 }
59
60 pub fn index(message: impl Into<String>) -> Self {
62 Self::Index(message.into())
63 }
64
65 pub fn query(message: impl Into<String>) -> Self {
67 Self::Query(message.into())
68 }
69
70 pub fn type_conversion(message: impl Into<String>) -> Self {
72 Self::TypeConversion(message.into())
73 }
74
75 pub fn config(message: impl Into<String>) -> Self {
77 Self::Config(message.into())
78 }
79
80 pub fn is_dimension_mismatch(&self) -> bool {
82 matches!(self, Self::DimensionMismatch { .. })
83 }
84
85 pub fn is_extension_not_installed(&self) -> bool {
87 matches!(self, Self::ExtensionNotInstalled)
88 }
89}
90
91impl From<VectorError> for QueryError {
92 fn from(err: VectorError) -> Self {
93 match err {
94 VectorError::ExtensionNotInstalled => {
95 QueryError::database("pgvector extension not installed".to_string())
96 }
97 VectorError::DimensionMismatch { expected, actual } => QueryError::invalid_input(
98 "vector",
99 format!("dimension mismatch: expected {expected}, got {actual}"),
100 ),
101 VectorError::EmptyVector => {
102 QueryError::invalid_input("vector", "empty vector".to_string())
103 }
104 VectorError::InvalidDimensions(msg) => QueryError::invalid_input("vector", msg),
105 VectorError::Index(msg) => QueryError::database(msg),
106 VectorError::Postgres(e) => QueryError::from(e),
107 VectorError::Query(msg) => QueryError::database(msg),
108 VectorError::TypeConversion(msg) => QueryError::serialization(msg),
109 VectorError::Config(msg) => QueryError::connection(msg),
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_error_creation() {
120 let err = VectorError::dimension_mismatch(3, 5);
121 assert!(err.is_dimension_mismatch());
122 assert!(err.to_string().contains("expected 3"));
123 assert!(err.to_string().contains("got 5"));
124 }
125
126 #[test]
127 fn test_extension_not_installed() {
128 let err = VectorError::ExtensionNotInstalled;
129 assert!(err.is_extension_not_installed());
130 assert!(err.to_string().contains("pgvector"));
131 }
132
133 #[test]
134 fn test_empty_vector() {
135 let err = VectorError::EmptyVector;
136 assert!(err.to_string().contains("empty vector"));
137 }
138
139 #[test]
140 fn test_into_query_error() {
141 let err = VectorError::dimension_mismatch(3, 5);
142 let query_err: QueryError = err.into();
143 assert!(query_err.to_string().contains("dimension mismatch"));
144 }
145
146 #[test]
147 fn test_index_error() {
148 let err = VectorError::index("failed to create HNSW index");
149 assert!(err.to_string().contains("HNSW"));
150 }
151
152 #[test]
153 fn test_config_error() {
154 let err = VectorError::config("invalid probes value");
155 assert!(err.to_string().contains("probes"));
156 }
157}