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}
82
83impl ErrorCode {
84 pub fn as_u32(self) -> u32 {
86 self as u32
87 }
88
89 pub fn from_u32(code: u32) -> Option<Self> {
93 match code {
94 0 => Some(Self::Unsupported),
95 1 => Some(Self::NamespaceNotFound),
96 2 => Some(Self::NamespaceAlreadyExists),
97 3 => Some(Self::NamespaceNotEmpty),
98 4 => Some(Self::TableNotFound),
99 5 => Some(Self::TableAlreadyExists),
100 6 => Some(Self::TableIndexNotFound),
101 7 => Some(Self::TableIndexAlreadyExists),
102 8 => Some(Self::TableTagNotFound),
103 9 => Some(Self::TableTagAlreadyExists),
104 10 => Some(Self::TransactionNotFound),
105 11 => Some(Self::TableVersionNotFound),
106 12 => Some(Self::TableColumnNotFound),
107 13 => Some(Self::InvalidInput),
108 14 => Some(Self::ConcurrentModification),
109 15 => Some(Self::PermissionDenied),
110 16 => Some(Self::Unauthenticated),
111 17 => Some(Self::ServiceUnavailable),
112 18 => Some(Self::Internal),
113 19 => Some(Self::InvalidTableState),
114 20 => Some(Self::TableSchemaValidationError),
115 21 => Some(Self::Throttling),
116 _ => None,
117 }
118 }
119}
120
121impl std::fmt::Display for ErrorCode {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 let name = match self {
124 Self::Unsupported => "Unsupported",
125 Self::NamespaceNotFound => "NamespaceNotFound",
126 Self::NamespaceAlreadyExists => "NamespaceAlreadyExists",
127 Self::NamespaceNotEmpty => "NamespaceNotEmpty",
128 Self::TableNotFound => "TableNotFound",
129 Self::TableAlreadyExists => "TableAlreadyExists",
130 Self::TableIndexNotFound => "TableIndexNotFound",
131 Self::TableIndexAlreadyExists => "TableIndexAlreadyExists",
132 Self::TableTagNotFound => "TableTagNotFound",
133 Self::TableTagAlreadyExists => "TableTagAlreadyExists",
134 Self::TransactionNotFound => "TransactionNotFound",
135 Self::TableVersionNotFound => "TableVersionNotFound",
136 Self::TableColumnNotFound => "TableColumnNotFound",
137 Self::InvalidInput => "InvalidInput",
138 Self::ConcurrentModification => "ConcurrentModification",
139 Self::PermissionDenied => "PermissionDenied",
140 Self::Unauthenticated => "Unauthenticated",
141 Self::ServiceUnavailable => "ServiceUnavailable",
142 Self::Internal => "Internal",
143 Self::InvalidTableState => "InvalidTableState",
144 Self::TableSchemaValidationError => "TableSchemaValidationError",
145 Self::Throttling => "Throttling",
146 };
147 write!(f, "{}", name)
148 }
149}
150
151#[derive(Debug, Snafu)]
174#[snafu(visibility(pub))]
175pub enum NamespaceError {
176 #[snafu(display("Unsupported: {message}"))]
178 Unsupported { message: String },
179
180 #[snafu(display("Namespace not found: {message}"))]
182 NamespaceNotFound { message: String },
183
184 #[snafu(display("Namespace already exists: {message}"))]
186 NamespaceAlreadyExists { message: String },
187
188 #[snafu(display("Namespace not empty: {message}"))]
190 NamespaceNotEmpty { message: String },
191
192 #[snafu(display("Table not found: {message}"))]
194 TableNotFound { message: String },
195
196 #[snafu(display("Table already exists: {message}"))]
198 TableAlreadyExists { message: String },
199
200 #[snafu(display("Table index not found: {message}"))]
202 TableIndexNotFound { message: String },
203
204 #[snafu(display("Table index already exists: {message}"))]
206 TableIndexAlreadyExists { message: String },
207
208 #[snafu(display("Table tag not found: {message}"))]
210 TableTagNotFound { message: String },
211
212 #[snafu(display("Table tag already exists: {message}"))]
214 TableTagAlreadyExists { message: String },
215
216 #[snafu(display("Transaction not found: {message}"))]
218 TransactionNotFound { message: String },
219
220 #[snafu(display("Table version not found: {message}"))]
222 TableVersionNotFound { message: String },
223
224 #[snafu(display("Table column not found: {message}"))]
226 TableColumnNotFound { message: String },
227
228 #[snafu(display("Invalid input: {message}"))]
230 InvalidInput { message: String },
231
232 #[snafu(display("Concurrent modification: {message}"))]
234 ConcurrentModification { message: String },
235
236 #[snafu(display("Permission denied: {message}"))]
238 PermissionDenied { message: String },
239
240 #[snafu(display("Unauthenticated: {message}"))]
242 Unauthenticated { message: String },
243
244 #[snafu(display("Service unavailable: {message}"))]
246 ServiceUnavailable { message: String },
247
248 #[snafu(display("Internal error: {message}"))]
250 Internal { message: String },
251
252 #[snafu(display("Invalid table state: {message}"))]
254 InvalidTableState { message: String },
255
256 #[snafu(display("Table schema validation error: {message}"))]
258 TableSchemaValidationError { message: String },
259
260 #[snafu(display("Throttling: {message}"))]
262 Throttling { message: String },
263}
264
265impl NamespaceError {
266 pub fn message(&self) -> &str {
272 match self {
273 Self::Unsupported { message }
274 | Self::NamespaceNotFound { message }
275 | Self::NamespaceAlreadyExists { message }
276 | Self::NamespaceNotEmpty { message }
277 | Self::TableNotFound { message }
278 | Self::TableAlreadyExists { message }
279 | Self::TableIndexNotFound { message }
280 | Self::TableIndexAlreadyExists { message }
281 | Self::TableTagNotFound { message }
282 | Self::TableTagAlreadyExists { message }
283 | Self::TransactionNotFound { message }
284 | Self::TableVersionNotFound { message }
285 | Self::TableColumnNotFound { message }
286 | Self::InvalidInput { message }
287 | Self::ConcurrentModification { message }
288 | Self::PermissionDenied { message }
289 | Self::Unauthenticated { message }
290 | Self::ServiceUnavailable { message }
291 | Self::Internal { message }
292 | Self::InvalidTableState { message }
293 | Self::TableSchemaValidationError { message }
294 | Self::Throttling { message } => message,
295 }
296 }
297
298 pub fn code(&self) -> ErrorCode {
302 match self {
303 Self::Unsupported { .. } => ErrorCode::Unsupported,
304 Self::NamespaceNotFound { .. } => ErrorCode::NamespaceNotFound,
305 Self::NamespaceAlreadyExists { .. } => ErrorCode::NamespaceAlreadyExists,
306 Self::NamespaceNotEmpty { .. } => ErrorCode::NamespaceNotEmpty,
307 Self::TableNotFound { .. } => ErrorCode::TableNotFound,
308 Self::TableAlreadyExists { .. } => ErrorCode::TableAlreadyExists,
309 Self::TableIndexNotFound { .. } => ErrorCode::TableIndexNotFound,
310 Self::TableIndexAlreadyExists { .. } => ErrorCode::TableIndexAlreadyExists,
311 Self::TableTagNotFound { .. } => ErrorCode::TableTagNotFound,
312 Self::TableTagAlreadyExists { .. } => ErrorCode::TableTagAlreadyExists,
313 Self::TransactionNotFound { .. } => ErrorCode::TransactionNotFound,
314 Self::TableVersionNotFound { .. } => ErrorCode::TableVersionNotFound,
315 Self::TableColumnNotFound { .. } => ErrorCode::TableColumnNotFound,
316 Self::InvalidInput { .. } => ErrorCode::InvalidInput,
317 Self::ConcurrentModification { .. } => ErrorCode::ConcurrentModification,
318 Self::PermissionDenied { .. } => ErrorCode::PermissionDenied,
319 Self::Unauthenticated { .. } => ErrorCode::Unauthenticated,
320 Self::ServiceUnavailable { .. } => ErrorCode::ServiceUnavailable,
321 Self::Internal { .. } => ErrorCode::Internal,
322 Self::InvalidTableState { .. } => ErrorCode::InvalidTableState,
323 Self::TableSchemaValidationError { .. } => ErrorCode::TableSchemaValidationError,
324 Self::Throttling { .. } => ErrorCode::Throttling,
325 }
326 }
327
328 pub fn from_code(code: u32, message: impl Into<String>) -> Self {
332 let message = message.into();
333 match ErrorCode::from_u32(code) {
334 Some(ErrorCode::Unsupported) => Self::Unsupported { message },
335 Some(ErrorCode::NamespaceNotFound) => Self::NamespaceNotFound { message },
336 Some(ErrorCode::NamespaceAlreadyExists) => Self::NamespaceAlreadyExists { message },
337 Some(ErrorCode::NamespaceNotEmpty) => Self::NamespaceNotEmpty { message },
338 Some(ErrorCode::TableNotFound) => Self::TableNotFound { message },
339 Some(ErrorCode::TableAlreadyExists) => Self::TableAlreadyExists { message },
340 Some(ErrorCode::TableIndexNotFound) => Self::TableIndexNotFound { message },
341 Some(ErrorCode::TableIndexAlreadyExists) => Self::TableIndexAlreadyExists { message },
342 Some(ErrorCode::TableTagNotFound) => Self::TableTagNotFound { message },
343 Some(ErrorCode::TableTagAlreadyExists) => Self::TableTagAlreadyExists { message },
344 Some(ErrorCode::TransactionNotFound) => Self::TransactionNotFound { message },
345 Some(ErrorCode::TableVersionNotFound) => Self::TableVersionNotFound { message },
346 Some(ErrorCode::TableColumnNotFound) => Self::TableColumnNotFound { message },
347 Some(ErrorCode::InvalidInput) => Self::InvalidInput { message },
348 Some(ErrorCode::ConcurrentModification) => Self::ConcurrentModification { message },
349 Some(ErrorCode::PermissionDenied) => Self::PermissionDenied { message },
350 Some(ErrorCode::Unauthenticated) => Self::Unauthenticated { message },
351 Some(ErrorCode::ServiceUnavailable) => Self::ServiceUnavailable { message },
352 Some(ErrorCode::Internal) => Self::Internal { message },
353 Some(ErrorCode::InvalidTableState) => Self::InvalidTableState { message },
354 Some(ErrorCode::TableSchemaValidationError) => {
355 Self::TableSchemaValidationError { message }
356 }
357 Some(ErrorCode::Throttling) => Self::Throttling { message },
358 None => Self::Internal { message },
359 }
360 }
361}
362
363impl From<NamespaceError> for lance_core::Error {
368 #[track_caller]
369 fn from(err: NamespaceError) -> Self {
370 Self::namespace_source(Box::new(err))
371 }
372}
373
374pub type Result<T> = std::result::Result<T, NamespaceError>;
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn test_error_code_roundtrip() {
383 for code in 0..=21 {
384 let error_code = ErrorCode::from_u32(code).unwrap();
385 assert_eq!(error_code.as_u32(), code);
386 }
387 }
388
389 #[test]
390 fn test_unknown_error_code() {
391 assert!(ErrorCode::from_u32(999).is_none());
392 }
393
394 #[test]
395 fn test_namespace_error_code() {
396 let err = NamespaceError::TableNotFound {
397 message: "test table".to_string(),
398 };
399 assert_eq!(err.code(), ErrorCode::TableNotFound);
400 assert_eq!(err.code().as_u32(), 4);
401 }
402
403 #[test]
404 fn test_from_code() {
405 let err = NamespaceError::from_code(4, "table not found");
406 assert_eq!(err.code(), ErrorCode::TableNotFound);
407 assert!(err.to_string().contains("table not found"));
408 }
409
410 #[test]
411 fn test_from_unknown_code() {
412 let err = NamespaceError::from_code(999, "unknown error");
413 assert_eq!(err.code(), ErrorCode::Internal);
414 }
415
416 #[test]
417 fn test_convert_to_lance_error() {
418 let ns_err = NamespaceError::TableNotFound {
419 message: "users".to_string(),
420 };
421 let lance_err: lance_core::Error = ns_err.into();
422
423 match &lance_err {
425 lance_core::Error::Namespace { source, .. } => {
426 let downcast = source.downcast_ref::<NamespaceError>();
428 assert!(downcast.is_some());
429 assert_eq!(downcast.unwrap().code(), ErrorCode::TableNotFound);
430 }
431 _ => panic!("Expected Namespace error"),
432 }
433 }
434
435 #[test]
436 fn test_error_display() {
437 let err = NamespaceError::TableNotFound {
438 message: "users".to_string(),
439 };
440 assert_eq!(err.to_string(), "Table not found: users");
441 }
442}