1use thiserror::Error;
6
7#[derive(Debug, Error)]
9pub enum DbxError {
10 #[error("storage error: {0}")]
12 Storage(String),
13
14 #[error("schema error: {0}")]
16 Schema(String),
17
18 #[error("arrow error: {source}")]
20 Arrow {
21 #[from]
22 source: arrow::error::ArrowError,
23 },
24
25 #[error("parquet error: {source}")]
27 Parquet {
28 #[from]
29 source: parquet::errors::ParquetError,
30 },
31
32 #[error("sled error: {source}")]
34 Sled {
35 #[from]
36 source: sled::Error,
37 },
38
39 #[error("io error: {source}")]
41 Io {
42 #[from]
43 source: std::io::Error,
44 },
45
46 #[error("table '{0}' not found")]
48 TableNotFound(String),
49
50 #[error("key not found")]
52 KeyNotFound,
53
54 #[error("type mismatch: expected {expected}, got {actual}")]
56 TypeMismatch { expected: String, actual: String },
57
58 #[error("constraint violation: {0}")]
60 ConstraintViolation(String),
61
62 #[error("serialization error: {0}")]
64 Serialization(String),
65
66 #[error("not implemented: {0}")]
68 NotImplemented(String),
69
70 #[error("SQL parse error: {message}\nSQL: {sql}")]
72 SqlParse { message: String, sql: String },
73
74 #[error("SQL execution error: {message}\nContext: {context}")]
76 SqlExecution { message: String, context: String },
77
78 #[error("SQL feature not supported: {feature}\nHint: {hint}")]
80 SqlNotSupported { feature: String, hint: String },
81
82 #[error("transaction conflict: {message}")]
84 TransactionConflict { message: String },
85
86 #[error("transaction aborted: {reason}")]
88 TransactionAborted { reason: String },
89
90 #[error("invalid operation: {message}\nContext: {context}")]
92 InvalidOperation { message: String, context: String },
93
94 #[error("index already exists on table '{table}', column '{column}'")]
96 IndexAlreadyExists { table: String, column: String },
97
98 #[error("index not found on table '{table}', column '{column}'")]
100 IndexNotFound { table: String, column: String },
101
102 #[error("WAL error: {0}")]
104 Wal(String),
105
106 #[error("checkpoint failed: {0}")]
108 CheckpointFailed(String),
109
110 #[error("recovery failed: {0}")]
112 RecoveryFailed(String),
113
114 #[error("encryption error: {0}")]
116 Encryption(String),
117
118 #[error("GPU error: {0}")]
120 Gpu(String),
121
122 #[error("callable '{0}' not found")]
124 CallableNotFound(String),
125
126 #[error("callable '{0}' already registered")]
128 DuplicateCallable(String),
129
130 #[error("invalid arguments: {0}")]
132 InvalidArguments(String),
133
134 #[error("lock poisoned")]
136 LockPoisoned,
137
138 #[error(
140 "performance regression detected for '{name}': baseline={baseline:.2}ms, current={current:.2}ms, ratio={ratio:.2}x"
141 )]
142 PerformanceRegression {
143 name: String,
144 baseline: f64,
145 current: f64,
146 ratio: f64,
147 },
148}
149
150pub type DbxResult<T> = Result<T, DbxError>;
152
153impl From<serde_json::Error> for DbxError {
155 fn from(err: serde_json::Error) -> Self {
156 DbxError::Serialization(err.to_string())
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn error_display_storage() {
166 let err = DbxError::Storage("disk full".to_string());
167 assert_eq!(err.to_string(), "storage error: disk full");
168 }
169
170 #[test]
171 fn error_display_table_not_found() {
172 let err = DbxError::TableNotFound("users".to_string());
173 assert_eq!(err.to_string(), "table 'users' not found");
174 }
175
176 #[test]
177 fn error_display_type_mismatch() {
178 let err = DbxError::TypeMismatch {
179 expected: "Int32".to_string(),
180 actual: "Utf8".to_string(),
181 };
182 assert_eq!(err.to_string(), "type mismatch: expected Int32, got Utf8");
183 }
184
185 #[test]
186 #[allow(clippy::unnecessary_literal_unwrap)]
187 fn dbx_result_ok() {
188 let result: DbxResult<i32> = Ok(42);
189 assert_eq!(result.unwrap(), 42);
190 }
191
192 #[test]
193 fn dbx_result_err() {
194 let result: DbxResult<i32> = Err(DbxError::KeyNotFound);
195 assert!(result.is_err());
196 }
197
198 #[test]
199 fn error_display_sql_parse() {
200 let err = DbxError::SqlParse {
201 message: "unexpected token".to_string(),
202 sql: "SELECT * FORM users".to_string(),
203 };
204 assert!(err.to_string().contains("SQL parse error"));
205 assert!(err.to_string().contains("FORM users"));
206 }
207
208 #[test]
209 fn error_display_sql_not_supported() {
210 let err = DbxError::SqlNotSupported {
211 feature: "WINDOW functions".to_string(),
212 hint: "Use subqueries instead".to_string(),
213 };
214 assert!(err.to_string().contains("not supported"));
215 assert!(err.to_string().contains("WINDOW functions"));
216 assert!(err.to_string().contains("subqueries"));
217 }
218
219 #[test]
220 fn error_display_transaction_conflict() {
221 let err = DbxError::TransactionConflict {
222 message: "write-write conflict on key 'user:123'".to_string(),
223 };
224 assert!(err.to_string().contains("transaction conflict"));
225 assert!(err.to_string().contains("user:123"));
226 }
227
228 #[test]
229 fn error_display_invalid_operation() {
230 let err = DbxError::InvalidOperation {
231 message: "cannot query after commit".to_string(),
232 context: "Transaction is in Committed state".to_string(),
233 };
234 assert!(err.to_string().contains("invalid operation"));
235 assert!(err.to_string().contains("after commit"));
236 }
237}