1use lance_core::error::ToSnafuLocation;
29use snafu::Snafu;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36#[repr(u32)]
37pub enum ErrorCode {
38 Unsupported = 0,
40 NamespaceNotFound = 1,
42 NamespaceAlreadyExists = 2,
44 NamespaceNotEmpty = 3,
46 TableNotFound = 4,
48 TableAlreadyExists = 5,
50 TableIndexNotFound = 6,
52 TableIndexAlreadyExists = 7,
54 TableTagNotFound = 8,
56 TableTagAlreadyExists = 9,
58 TransactionNotFound = 10,
60 TableVersionNotFound = 11,
62 TableColumnNotFound = 12,
64 InvalidInput = 13,
66 ConcurrentModification = 14,
68 PermissionDenied = 15,
70 Unauthenticated = 16,
72 ServiceUnavailable = 17,
74 Internal = 18,
76 InvalidTableState = 19,
78 TableSchemaValidationError = 20,
80}
81
82impl ErrorCode {
83 pub fn as_u32(self) -> u32 {
85 self as u32
86 }
87
88 pub fn from_u32(code: u32) -> Option<Self> {
92 match code {
93 0 => Some(Self::Unsupported),
94 1 => Some(Self::NamespaceNotFound),
95 2 => Some(Self::NamespaceAlreadyExists),
96 3 => Some(Self::NamespaceNotEmpty),
97 4 => Some(Self::TableNotFound),
98 5 => Some(Self::TableAlreadyExists),
99 6 => Some(Self::TableIndexNotFound),
100 7 => Some(Self::TableIndexAlreadyExists),
101 8 => Some(Self::TableTagNotFound),
102 9 => Some(Self::TableTagAlreadyExists),
103 10 => Some(Self::TransactionNotFound),
104 11 => Some(Self::TableVersionNotFound),
105 12 => Some(Self::TableColumnNotFound),
106 13 => Some(Self::InvalidInput),
107 14 => Some(Self::ConcurrentModification),
108 15 => Some(Self::PermissionDenied),
109 16 => Some(Self::Unauthenticated),
110 17 => Some(Self::ServiceUnavailable),
111 18 => Some(Self::Internal),
112 19 => Some(Self::InvalidTableState),
113 20 => Some(Self::TableSchemaValidationError),
114 _ => None,
115 }
116 }
117}
118
119impl std::fmt::Display for ErrorCode {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 let name = match self {
122 Self::Unsupported => "Unsupported",
123 Self::NamespaceNotFound => "NamespaceNotFound",
124 Self::NamespaceAlreadyExists => "NamespaceAlreadyExists",
125 Self::NamespaceNotEmpty => "NamespaceNotEmpty",
126 Self::TableNotFound => "TableNotFound",
127 Self::TableAlreadyExists => "TableAlreadyExists",
128 Self::TableIndexNotFound => "TableIndexNotFound",
129 Self::TableIndexAlreadyExists => "TableIndexAlreadyExists",
130 Self::TableTagNotFound => "TableTagNotFound",
131 Self::TableTagAlreadyExists => "TableTagAlreadyExists",
132 Self::TransactionNotFound => "TransactionNotFound",
133 Self::TableVersionNotFound => "TableVersionNotFound",
134 Self::TableColumnNotFound => "TableColumnNotFound",
135 Self::InvalidInput => "InvalidInput",
136 Self::ConcurrentModification => "ConcurrentModification",
137 Self::PermissionDenied => "PermissionDenied",
138 Self::Unauthenticated => "Unauthenticated",
139 Self::ServiceUnavailable => "ServiceUnavailable",
140 Self::Internal => "Internal",
141 Self::InvalidTableState => "InvalidTableState",
142 Self::TableSchemaValidationError => "TableSchemaValidationError",
143 };
144 write!(f, "{}", name)
145 }
146}
147
148#[derive(Debug, Snafu)]
171#[snafu(visibility(pub))]
172pub enum NamespaceError {
173 #[snafu(display("Unsupported: {message}"))]
175 Unsupported { message: String },
176
177 #[snafu(display("Namespace not found: {message}"))]
179 NamespaceNotFound { message: String },
180
181 #[snafu(display("Namespace already exists: {message}"))]
183 NamespaceAlreadyExists { message: String },
184
185 #[snafu(display("Namespace not empty: {message}"))]
187 NamespaceNotEmpty { message: String },
188
189 #[snafu(display("Table not found: {message}"))]
191 TableNotFound { message: String },
192
193 #[snafu(display("Table already exists: {message}"))]
195 TableAlreadyExists { message: String },
196
197 #[snafu(display("Table index not found: {message}"))]
199 TableIndexNotFound { message: String },
200
201 #[snafu(display("Table index already exists: {message}"))]
203 TableIndexAlreadyExists { message: String },
204
205 #[snafu(display("Table tag not found: {message}"))]
207 TableTagNotFound { message: String },
208
209 #[snafu(display("Table tag already exists: {message}"))]
211 TableTagAlreadyExists { message: String },
212
213 #[snafu(display("Transaction not found: {message}"))]
215 TransactionNotFound { message: String },
216
217 #[snafu(display("Table version not found: {message}"))]
219 TableVersionNotFound { message: String },
220
221 #[snafu(display("Table column not found: {message}"))]
223 TableColumnNotFound { message: String },
224
225 #[snafu(display("Invalid input: {message}"))]
227 InvalidInput { message: String },
228
229 #[snafu(display("Concurrent modification: {message}"))]
231 ConcurrentModification { message: String },
232
233 #[snafu(display("Permission denied: {message}"))]
235 PermissionDenied { message: String },
236
237 #[snafu(display("Unauthenticated: {message}"))]
239 Unauthenticated { message: String },
240
241 #[snafu(display("Service unavailable: {message}"))]
243 ServiceUnavailable { message: String },
244
245 #[snafu(display("Internal error: {message}"))]
247 Internal { message: String },
248
249 #[snafu(display("Invalid table state: {message}"))]
251 InvalidTableState { message: String },
252
253 #[snafu(display("Table schema validation error: {message}"))]
255 TableSchemaValidationError { message: String },
256}
257
258impl NamespaceError {
259 pub fn code(&self) -> ErrorCode {
263 match self {
264 Self::Unsupported { .. } => ErrorCode::Unsupported,
265 Self::NamespaceNotFound { .. } => ErrorCode::NamespaceNotFound,
266 Self::NamespaceAlreadyExists { .. } => ErrorCode::NamespaceAlreadyExists,
267 Self::NamespaceNotEmpty { .. } => ErrorCode::NamespaceNotEmpty,
268 Self::TableNotFound { .. } => ErrorCode::TableNotFound,
269 Self::TableAlreadyExists { .. } => ErrorCode::TableAlreadyExists,
270 Self::TableIndexNotFound { .. } => ErrorCode::TableIndexNotFound,
271 Self::TableIndexAlreadyExists { .. } => ErrorCode::TableIndexAlreadyExists,
272 Self::TableTagNotFound { .. } => ErrorCode::TableTagNotFound,
273 Self::TableTagAlreadyExists { .. } => ErrorCode::TableTagAlreadyExists,
274 Self::TransactionNotFound { .. } => ErrorCode::TransactionNotFound,
275 Self::TableVersionNotFound { .. } => ErrorCode::TableVersionNotFound,
276 Self::TableColumnNotFound { .. } => ErrorCode::TableColumnNotFound,
277 Self::InvalidInput { .. } => ErrorCode::InvalidInput,
278 Self::ConcurrentModification { .. } => ErrorCode::ConcurrentModification,
279 Self::PermissionDenied { .. } => ErrorCode::PermissionDenied,
280 Self::Unauthenticated { .. } => ErrorCode::Unauthenticated,
281 Self::ServiceUnavailable { .. } => ErrorCode::ServiceUnavailable,
282 Self::Internal { .. } => ErrorCode::Internal,
283 Self::InvalidTableState { .. } => ErrorCode::InvalidTableState,
284 Self::TableSchemaValidationError { .. } => ErrorCode::TableSchemaValidationError,
285 }
286 }
287
288 pub fn from_code(code: u32, message: impl Into<String>) -> Self {
292 let message = message.into();
293 match ErrorCode::from_u32(code) {
294 Some(ErrorCode::Unsupported) => Self::Unsupported { message },
295 Some(ErrorCode::NamespaceNotFound) => Self::NamespaceNotFound { message },
296 Some(ErrorCode::NamespaceAlreadyExists) => Self::NamespaceAlreadyExists { message },
297 Some(ErrorCode::NamespaceNotEmpty) => Self::NamespaceNotEmpty { message },
298 Some(ErrorCode::TableNotFound) => Self::TableNotFound { message },
299 Some(ErrorCode::TableAlreadyExists) => Self::TableAlreadyExists { message },
300 Some(ErrorCode::TableIndexNotFound) => Self::TableIndexNotFound { message },
301 Some(ErrorCode::TableIndexAlreadyExists) => Self::TableIndexAlreadyExists { message },
302 Some(ErrorCode::TableTagNotFound) => Self::TableTagNotFound { message },
303 Some(ErrorCode::TableTagAlreadyExists) => Self::TableTagAlreadyExists { message },
304 Some(ErrorCode::TransactionNotFound) => Self::TransactionNotFound { message },
305 Some(ErrorCode::TableVersionNotFound) => Self::TableVersionNotFound { message },
306 Some(ErrorCode::TableColumnNotFound) => Self::TableColumnNotFound { message },
307 Some(ErrorCode::InvalidInput) => Self::InvalidInput { message },
308 Some(ErrorCode::ConcurrentModification) => Self::ConcurrentModification { message },
309 Some(ErrorCode::PermissionDenied) => Self::PermissionDenied { message },
310 Some(ErrorCode::Unauthenticated) => Self::Unauthenticated { message },
311 Some(ErrorCode::ServiceUnavailable) => Self::ServiceUnavailable { message },
312 Some(ErrorCode::Internal) => Self::Internal { message },
313 Some(ErrorCode::InvalidTableState) => Self::InvalidTableState { message },
314 Some(ErrorCode::TableSchemaValidationError) => {
315 Self::TableSchemaValidationError { message }
316 }
317 None => Self::Internal { message },
318 }
319 }
320}
321
322impl From<NamespaceError> for lance_core::Error {
327 #[track_caller]
328 fn from(err: NamespaceError) -> Self {
329 Self::Namespace {
330 source: Box::new(err),
331 location: std::panic::Location::caller().to_snafu_location(),
332 }
333 }
334}
335
336pub type Result<T> = std::result::Result<T, NamespaceError>;
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn test_error_code_roundtrip() {
345 for code in 0..=20 {
346 let error_code = ErrorCode::from_u32(code).unwrap();
347 assert_eq!(error_code.as_u32(), code);
348 }
349 }
350
351 #[test]
352 fn test_unknown_error_code() {
353 assert!(ErrorCode::from_u32(999).is_none());
354 }
355
356 #[test]
357 fn test_namespace_error_code() {
358 let err = NamespaceError::TableNotFound {
359 message: "test table".to_string(),
360 };
361 assert_eq!(err.code(), ErrorCode::TableNotFound);
362 assert_eq!(err.code().as_u32(), 4);
363 }
364
365 #[test]
366 fn test_from_code() {
367 let err = NamespaceError::from_code(4, "table not found");
368 assert_eq!(err.code(), ErrorCode::TableNotFound);
369 assert!(err.to_string().contains("table not found"));
370 }
371
372 #[test]
373 fn test_from_unknown_code() {
374 let err = NamespaceError::from_code(999, "unknown error");
375 assert_eq!(err.code(), ErrorCode::Internal);
376 }
377
378 #[test]
379 fn test_convert_to_lance_error() {
380 let ns_err = NamespaceError::TableNotFound {
381 message: "users".to_string(),
382 };
383 let lance_err: lance_core::Error = ns_err.into();
384
385 match &lance_err {
387 lance_core::Error::Namespace { source, .. } => {
388 let downcast = source.downcast_ref::<NamespaceError>();
390 assert!(downcast.is_some());
391 assert_eq!(downcast.unwrap().code(), ErrorCode::TableNotFound);
392 }
393 _ => panic!("Expected Namespace error"),
394 }
395 }
396
397 #[test]
398 fn test_error_display() {
399 let err = NamespaceError::TableNotFound {
400 message: "users".to_string(),
401 };
402 assert_eq!(err.to_string(), "Table not found: users");
403 }
404}