1use snafu::Snafu;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35#[repr(u32)]
36pub enum ErrorCode {
37 Unsupported = 0,
39 NamespaceNotFound = 1,
41 NamespaceAlreadyExists = 2,
43 NamespaceNotEmpty = 3,
45 TableNotFound = 4,
47 TableAlreadyExists = 5,
49 TableIndexNotFound = 6,
51 TableIndexAlreadyExists = 7,
53 TableTagNotFound = 8,
55 TableTagAlreadyExists = 9,
57 TransactionNotFound = 10,
59 TableVersionNotFound = 11,
61 TableColumnNotFound = 12,
63 InvalidInput = 13,
65 ConcurrentModification = 14,
67 PermissionDenied = 15,
69 Unauthenticated = 16,
71 ServiceUnavailable = 17,
73 Internal = 18,
75 InvalidTableState = 19,
77 TableSchemaValidationError = 20,
79 Throttling = 21,
81 TableBranchNotFound = 22,
83 TableBranchAlreadyExists = 23,
85}
86
87impl ErrorCode {
88 pub fn as_u32(self) -> u32 {
90 self as u32
91 }
92
93 pub fn from_u32(code: u32) -> Option<Self> {
97 match code {
98 0 => Some(Self::Unsupported),
99 1 => Some(Self::NamespaceNotFound),
100 2 => Some(Self::NamespaceAlreadyExists),
101 3 => Some(Self::NamespaceNotEmpty),
102 4 => Some(Self::TableNotFound),
103 5 => Some(Self::TableAlreadyExists),
104 6 => Some(Self::TableIndexNotFound),
105 7 => Some(Self::TableIndexAlreadyExists),
106 8 => Some(Self::TableTagNotFound),
107 9 => Some(Self::TableTagAlreadyExists),
108 10 => Some(Self::TransactionNotFound),
109 11 => Some(Self::TableVersionNotFound),
110 12 => Some(Self::TableColumnNotFound),
111 13 => Some(Self::InvalidInput),
112 14 => Some(Self::ConcurrentModification),
113 15 => Some(Self::PermissionDenied),
114 16 => Some(Self::Unauthenticated),
115 17 => Some(Self::ServiceUnavailable),
116 18 => Some(Self::Internal),
117 19 => Some(Self::InvalidTableState),
118 20 => Some(Self::TableSchemaValidationError),
119 21 => Some(Self::Throttling),
120 22 => Some(Self::TableBranchNotFound),
121 23 => Some(Self::TableBranchAlreadyExists),
122 _ => None,
123 }
124 }
125}
126
127impl std::fmt::Display for ErrorCode {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 let name = match self {
130 Self::Unsupported => "Unsupported",
131 Self::NamespaceNotFound => "NamespaceNotFound",
132 Self::NamespaceAlreadyExists => "NamespaceAlreadyExists",
133 Self::NamespaceNotEmpty => "NamespaceNotEmpty",
134 Self::TableNotFound => "TableNotFound",
135 Self::TableAlreadyExists => "TableAlreadyExists",
136 Self::TableIndexNotFound => "TableIndexNotFound",
137 Self::TableIndexAlreadyExists => "TableIndexAlreadyExists",
138 Self::TableTagNotFound => "TableTagNotFound",
139 Self::TableTagAlreadyExists => "TableTagAlreadyExists",
140 Self::TransactionNotFound => "TransactionNotFound",
141 Self::TableVersionNotFound => "TableVersionNotFound",
142 Self::TableColumnNotFound => "TableColumnNotFound",
143 Self::InvalidInput => "InvalidInput",
144 Self::ConcurrentModification => "ConcurrentModification",
145 Self::PermissionDenied => "PermissionDenied",
146 Self::Unauthenticated => "Unauthenticated",
147 Self::ServiceUnavailable => "ServiceUnavailable",
148 Self::Internal => "Internal",
149 Self::InvalidTableState => "InvalidTableState",
150 Self::TableSchemaValidationError => "TableSchemaValidationError",
151 Self::Throttling => "Throttling",
152 Self::TableBranchNotFound => "TableBranchNotFound",
153 Self::TableBranchAlreadyExists => "TableBranchAlreadyExists",
154 };
155 write!(f, "{}", name)
156 }
157}
158
159#[derive(Debug, Snafu)]
182#[snafu(visibility(pub))]
183pub enum NamespaceError {
184 #[snafu(display("Unsupported: {message}"))]
186 Unsupported { message: String },
187
188 #[snafu(display("Namespace not found: {message}"))]
190 NamespaceNotFound { message: String },
191
192 #[snafu(display("Namespace already exists: {message}"))]
194 NamespaceAlreadyExists { message: String },
195
196 #[snafu(display("Namespace not empty: {message}"))]
198 NamespaceNotEmpty { message: String },
199
200 #[snafu(display("Table not found: {message}"))]
202 TableNotFound { message: String },
203
204 #[snafu(display("Table already exists: {message}"))]
206 TableAlreadyExists { message: String },
207
208 #[snafu(display("Table index not found: {message}"))]
210 TableIndexNotFound { message: String },
211
212 #[snafu(display("Table index already exists: {message}"))]
214 TableIndexAlreadyExists { message: String },
215
216 #[snafu(display("Table tag not found: {message}"))]
218 TableTagNotFound { message: String },
219
220 #[snafu(display("Table tag already exists: {message}"))]
222 TableTagAlreadyExists { message: String },
223
224 #[snafu(display("Transaction not found: {message}"))]
226 TransactionNotFound { message: String },
227
228 #[snafu(display("Table version not found: {message}"))]
230 TableVersionNotFound { message: String },
231
232 #[snafu(display("Table column not found: {message}"))]
234 TableColumnNotFound { message: String },
235
236 #[snafu(display("Invalid input: {message}"))]
238 InvalidInput { message: String },
239
240 #[snafu(display("Concurrent modification: {message}"))]
242 ConcurrentModification { message: String },
243
244 #[snafu(display("Permission denied: {message}"))]
246 PermissionDenied { message: String },
247
248 #[snafu(display("Unauthenticated: {message}"))]
250 Unauthenticated { message: String },
251
252 #[snafu(display("Service unavailable: {message}"))]
254 ServiceUnavailable { message: String },
255
256 #[snafu(display("Internal error: {message}"))]
258 Internal { message: String },
259
260 #[snafu(display("Invalid table state: {message}"))]
262 InvalidTableState { message: String },
263
264 #[snafu(display("Table schema validation error: {message}"))]
266 TableSchemaValidationError { message: String },
267
268 #[snafu(display("Throttling: {message}"))]
270 Throttling { message: String },
271
272 #[snafu(display("Table branch not found: {message}"))]
274 TableBranchNotFound { message: String },
275
276 #[snafu(display("Table branch already exists: {message}"))]
278 TableBranchAlreadyExists { message: String },
279}
280
281impl NamespaceError {
282 pub fn message(&self) -> &str {
288 match self {
289 Self::Unsupported { message }
290 | Self::NamespaceNotFound { message }
291 | Self::NamespaceAlreadyExists { message }
292 | Self::NamespaceNotEmpty { message }
293 | Self::TableNotFound { message }
294 | Self::TableAlreadyExists { message }
295 | Self::TableIndexNotFound { message }
296 | Self::TableIndexAlreadyExists { message }
297 | Self::TableTagNotFound { message }
298 | Self::TableTagAlreadyExists { message }
299 | Self::TransactionNotFound { message }
300 | Self::TableVersionNotFound { message }
301 | Self::TableColumnNotFound { message }
302 | Self::InvalidInput { message }
303 | Self::ConcurrentModification { message }
304 | Self::PermissionDenied { message }
305 | Self::Unauthenticated { message }
306 | Self::ServiceUnavailable { message }
307 | Self::Internal { message }
308 | Self::InvalidTableState { message }
309 | Self::TableSchemaValidationError { message }
310 | Self::Throttling { message }
311 | Self::TableBranchNotFound { message }
312 | Self::TableBranchAlreadyExists { message } => message,
313 }
314 }
315
316 pub fn code(&self) -> ErrorCode {
320 match self {
321 Self::Unsupported { .. } => ErrorCode::Unsupported,
322 Self::NamespaceNotFound { .. } => ErrorCode::NamespaceNotFound,
323 Self::NamespaceAlreadyExists { .. } => ErrorCode::NamespaceAlreadyExists,
324 Self::NamespaceNotEmpty { .. } => ErrorCode::NamespaceNotEmpty,
325 Self::TableNotFound { .. } => ErrorCode::TableNotFound,
326 Self::TableAlreadyExists { .. } => ErrorCode::TableAlreadyExists,
327 Self::TableIndexNotFound { .. } => ErrorCode::TableIndexNotFound,
328 Self::TableIndexAlreadyExists { .. } => ErrorCode::TableIndexAlreadyExists,
329 Self::TableTagNotFound { .. } => ErrorCode::TableTagNotFound,
330 Self::TableTagAlreadyExists { .. } => ErrorCode::TableTagAlreadyExists,
331 Self::TransactionNotFound { .. } => ErrorCode::TransactionNotFound,
332 Self::TableVersionNotFound { .. } => ErrorCode::TableVersionNotFound,
333 Self::TableColumnNotFound { .. } => ErrorCode::TableColumnNotFound,
334 Self::InvalidInput { .. } => ErrorCode::InvalidInput,
335 Self::ConcurrentModification { .. } => ErrorCode::ConcurrentModification,
336 Self::PermissionDenied { .. } => ErrorCode::PermissionDenied,
337 Self::Unauthenticated { .. } => ErrorCode::Unauthenticated,
338 Self::ServiceUnavailable { .. } => ErrorCode::ServiceUnavailable,
339 Self::Internal { .. } => ErrorCode::Internal,
340 Self::InvalidTableState { .. } => ErrorCode::InvalidTableState,
341 Self::TableSchemaValidationError { .. } => ErrorCode::TableSchemaValidationError,
342 Self::Throttling { .. } => ErrorCode::Throttling,
343 Self::TableBranchNotFound { .. } => ErrorCode::TableBranchNotFound,
344 Self::TableBranchAlreadyExists { .. } => ErrorCode::TableBranchAlreadyExists,
345 }
346 }
347
348 pub fn from_code(code: u32, message: impl Into<String>) -> Self {
352 let message = message.into();
353 match ErrorCode::from_u32(code) {
354 Some(ErrorCode::Unsupported) => Self::Unsupported { message },
355 Some(ErrorCode::NamespaceNotFound) => Self::NamespaceNotFound { message },
356 Some(ErrorCode::NamespaceAlreadyExists) => Self::NamespaceAlreadyExists { message },
357 Some(ErrorCode::NamespaceNotEmpty) => Self::NamespaceNotEmpty { message },
358 Some(ErrorCode::TableNotFound) => Self::TableNotFound { message },
359 Some(ErrorCode::TableAlreadyExists) => Self::TableAlreadyExists { message },
360 Some(ErrorCode::TableIndexNotFound) => Self::TableIndexNotFound { message },
361 Some(ErrorCode::TableIndexAlreadyExists) => Self::TableIndexAlreadyExists { message },
362 Some(ErrorCode::TableTagNotFound) => Self::TableTagNotFound { message },
363 Some(ErrorCode::TableTagAlreadyExists) => Self::TableTagAlreadyExists { message },
364 Some(ErrorCode::TransactionNotFound) => Self::TransactionNotFound { message },
365 Some(ErrorCode::TableVersionNotFound) => Self::TableVersionNotFound { message },
366 Some(ErrorCode::TableColumnNotFound) => Self::TableColumnNotFound { message },
367 Some(ErrorCode::InvalidInput) => Self::InvalidInput { message },
368 Some(ErrorCode::ConcurrentModification) => Self::ConcurrentModification { message },
369 Some(ErrorCode::PermissionDenied) => Self::PermissionDenied { message },
370 Some(ErrorCode::Unauthenticated) => Self::Unauthenticated { message },
371 Some(ErrorCode::ServiceUnavailable) => Self::ServiceUnavailable { message },
372 Some(ErrorCode::Internal) => Self::Internal { message },
373 Some(ErrorCode::InvalidTableState) => Self::InvalidTableState { message },
374 Some(ErrorCode::TableSchemaValidationError) => {
375 Self::TableSchemaValidationError { message }
376 }
377 Some(ErrorCode::Throttling) => Self::Throttling { message },
378 Some(ErrorCode::TableBranchNotFound) => Self::TableBranchNotFound { message },
379 Some(ErrorCode::TableBranchAlreadyExists) => Self::TableBranchAlreadyExists { message },
380 None => Self::Internal { message },
381 }
382 }
383}
384
385impl From<NamespaceError> for lance_core::Error {
390 #[track_caller]
391 fn from(err: NamespaceError) -> Self {
392 Self::namespace_source(Box::new(err))
393 }
394}
395
396pub type Result<T> = std::result::Result<T, NamespaceError>;
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 #[test]
404 fn test_error_code_roundtrip() {
405 for code in 0..=23 {
406 let error_code = ErrorCode::from_u32(code).unwrap();
407 assert_eq!(error_code.as_u32(), code);
408 }
409 }
410
411 #[test]
412 fn test_unknown_error_code() {
413 assert!(ErrorCode::from_u32(999).is_none());
414 }
415
416 #[test]
417 fn test_namespace_error_code() {
418 let err = NamespaceError::TableNotFound {
419 message: "test table".to_string(),
420 };
421 assert_eq!(err.code(), ErrorCode::TableNotFound);
422 assert_eq!(err.code().as_u32(), 4);
423 }
424
425 #[test]
426 fn test_from_code() {
427 let err = NamespaceError::from_code(4, "table not found");
428 assert_eq!(err.code(), ErrorCode::TableNotFound);
429 assert!(err.to_string().contains("table not found"));
430 }
431
432 #[test]
433 fn test_from_unknown_code() {
434 let err = NamespaceError::from_code(999, "unknown error");
435 assert_eq!(err.code(), ErrorCode::Internal);
436 }
437
438 #[test]
439 fn test_convert_to_lance_error() {
440 let ns_err = NamespaceError::TableNotFound {
441 message: "users".to_string(),
442 };
443 let lance_err: lance_core::Error = ns_err.into();
444
445 match &lance_err {
447 lance_core::Error::Namespace { source, .. } => {
448 let downcast = source.downcast_ref::<NamespaceError>();
450 assert!(downcast.is_some());
451 assert_eq!(downcast.unwrap().code(), ErrorCode::TableNotFound);
452 }
453 _ => panic!("Expected Namespace error"),
454 }
455 }
456
457 #[test]
458 fn test_error_display() {
459 let err = NamespaceError::TableNotFound {
460 message: "users".to_string(),
461 };
462 assert_eq!(err.to_string(), "Table not found: users");
463 }
464}