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 Throttled = 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::Throttled),
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::Throttled => "Throttled",
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("Throttled: {message}"))]
262 Throttled { message: String },
263}
264
265impl NamespaceError {
266 pub fn code(&self) -> ErrorCode {
270 match self {
271 Self::Unsupported { .. } => ErrorCode::Unsupported,
272 Self::NamespaceNotFound { .. } => ErrorCode::NamespaceNotFound,
273 Self::NamespaceAlreadyExists { .. } => ErrorCode::NamespaceAlreadyExists,
274 Self::NamespaceNotEmpty { .. } => ErrorCode::NamespaceNotEmpty,
275 Self::TableNotFound { .. } => ErrorCode::TableNotFound,
276 Self::TableAlreadyExists { .. } => ErrorCode::TableAlreadyExists,
277 Self::TableIndexNotFound { .. } => ErrorCode::TableIndexNotFound,
278 Self::TableIndexAlreadyExists { .. } => ErrorCode::TableIndexAlreadyExists,
279 Self::TableTagNotFound { .. } => ErrorCode::TableTagNotFound,
280 Self::TableTagAlreadyExists { .. } => ErrorCode::TableTagAlreadyExists,
281 Self::TransactionNotFound { .. } => ErrorCode::TransactionNotFound,
282 Self::TableVersionNotFound { .. } => ErrorCode::TableVersionNotFound,
283 Self::TableColumnNotFound { .. } => ErrorCode::TableColumnNotFound,
284 Self::InvalidInput { .. } => ErrorCode::InvalidInput,
285 Self::ConcurrentModification { .. } => ErrorCode::ConcurrentModification,
286 Self::PermissionDenied { .. } => ErrorCode::PermissionDenied,
287 Self::Unauthenticated { .. } => ErrorCode::Unauthenticated,
288 Self::ServiceUnavailable { .. } => ErrorCode::ServiceUnavailable,
289 Self::Internal { .. } => ErrorCode::Internal,
290 Self::InvalidTableState { .. } => ErrorCode::InvalidTableState,
291 Self::TableSchemaValidationError { .. } => ErrorCode::TableSchemaValidationError,
292 Self::Throttled { .. } => ErrorCode::Throttled,
293 }
294 }
295
296 pub fn from_code(code: u32, message: impl Into<String>) -> Self {
300 let message = message.into();
301 match ErrorCode::from_u32(code) {
302 Some(ErrorCode::Unsupported) => Self::Unsupported { message },
303 Some(ErrorCode::NamespaceNotFound) => Self::NamespaceNotFound { message },
304 Some(ErrorCode::NamespaceAlreadyExists) => Self::NamespaceAlreadyExists { message },
305 Some(ErrorCode::NamespaceNotEmpty) => Self::NamespaceNotEmpty { message },
306 Some(ErrorCode::TableNotFound) => Self::TableNotFound { message },
307 Some(ErrorCode::TableAlreadyExists) => Self::TableAlreadyExists { message },
308 Some(ErrorCode::TableIndexNotFound) => Self::TableIndexNotFound { message },
309 Some(ErrorCode::TableIndexAlreadyExists) => Self::TableIndexAlreadyExists { message },
310 Some(ErrorCode::TableTagNotFound) => Self::TableTagNotFound { message },
311 Some(ErrorCode::TableTagAlreadyExists) => Self::TableTagAlreadyExists { message },
312 Some(ErrorCode::TransactionNotFound) => Self::TransactionNotFound { message },
313 Some(ErrorCode::TableVersionNotFound) => Self::TableVersionNotFound { message },
314 Some(ErrorCode::TableColumnNotFound) => Self::TableColumnNotFound { message },
315 Some(ErrorCode::InvalidInput) => Self::InvalidInput { message },
316 Some(ErrorCode::ConcurrentModification) => Self::ConcurrentModification { message },
317 Some(ErrorCode::PermissionDenied) => Self::PermissionDenied { message },
318 Some(ErrorCode::Unauthenticated) => Self::Unauthenticated { message },
319 Some(ErrorCode::ServiceUnavailable) => Self::ServiceUnavailable { message },
320 Some(ErrorCode::Internal) => Self::Internal { message },
321 Some(ErrorCode::InvalidTableState) => Self::InvalidTableState { message },
322 Some(ErrorCode::TableSchemaValidationError) => {
323 Self::TableSchemaValidationError { message }
324 }
325 Some(ErrorCode::Throttled) => Self::Throttled { message },
326 None => Self::Internal { message },
327 }
328 }
329}
330
331impl From<NamespaceError> for lance_core::Error {
336 #[track_caller]
337 fn from(err: NamespaceError) -> Self {
338 Self::namespace_source(Box::new(err))
339 }
340}
341
342pub type Result<T> = std::result::Result<T, NamespaceError>;
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348
349 #[test]
350 fn test_error_code_roundtrip() {
351 for code in 0..=21 {
352 let error_code = ErrorCode::from_u32(code).unwrap();
353 assert_eq!(error_code.as_u32(), code);
354 }
355 }
356
357 #[test]
358 fn test_unknown_error_code() {
359 assert!(ErrorCode::from_u32(999).is_none());
360 }
361
362 #[test]
363 fn test_namespace_error_code() {
364 let err = NamespaceError::TableNotFound {
365 message: "test table".to_string(),
366 };
367 assert_eq!(err.code(), ErrorCode::TableNotFound);
368 assert_eq!(err.code().as_u32(), 4);
369 }
370
371 #[test]
372 fn test_from_code() {
373 let err = NamespaceError::from_code(4, "table not found");
374 assert_eq!(err.code(), ErrorCode::TableNotFound);
375 assert!(err.to_string().contains("table not found"));
376 }
377
378 #[test]
379 fn test_from_unknown_code() {
380 let err = NamespaceError::from_code(999, "unknown error");
381 assert_eq!(err.code(), ErrorCode::Internal);
382 }
383
384 #[test]
385 fn test_convert_to_lance_error() {
386 let ns_err = NamespaceError::TableNotFound {
387 message: "users".to_string(),
388 };
389 let lance_err: lance_core::Error = ns_err.into();
390
391 match &lance_err {
393 lance_core::Error::Namespace { source, .. } => {
394 let downcast = source.downcast_ref::<NamespaceError>();
396 assert!(downcast.is_some());
397 assert_eq!(downcast.unwrap().code(), ErrorCode::TableNotFound);
398 }
399 _ => panic!("Expected Namespace error"),
400 }
401 }
402
403 #[test]
404 fn test_error_display() {
405 let err = NamespaceError::TableNotFound {
406 message: "users".to_string(),
407 };
408 assert_eq!(err.to_string(), "Table not found: users");
409 }
410}