use snafu::Snafu;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum ErrorCode {
Unsupported = 0,
NamespaceNotFound = 1,
NamespaceAlreadyExists = 2,
NamespaceNotEmpty = 3,
TableNotFound = 4,
TableAlreadyExists = 5,
TableIndexNotFound = 6,
TableIndexAlreadyExists = 7,
TableTagNotFound = 8,
TableTagAlreadyExists = 9,
TransactionNotFound = 10,
TableVersionNotFound = 11,
TableColumnNotFound = 12,
InvalidInput = 13,
ConcurrentModification = 14,
PermissionDenied = 15,
Unauthenticated = 16,
ServiceUnavailable = 17,
Internal = 18,
InvalidTableState = 19,
TableSchemaValidationError = 20,
Throttled = 21,
}
impl ErrorCode {
pub fn as_u32(self) -> u32 {
self as u32
}
pub fn from_u32(code: u32) -> Option<Self> {
match code {
0 => Some(Self::Unsupported),
1 => Some(Self::NamespaceNotFound),
2 => Some(Self::NamespaceAlreadyExists),
3 => Some(Self::NamespaceNotEmpty),
4 => Some(Self::TableNotFound),
5 => Some(Self::TableAlreadyExists),
6 => Some(Self::TableIndexNotFound),
7 => Some(Self::TableIndexAlreadyExists),
8 => Some(Self::TableTagNotFound),
9 => Some(Self::TableTagAlreadyExists),
10 => Some(Self::TransactionNotFound),
11 => Some(Self::TableVersionNotFound),
12 => Some(Self::TableColumnNotFound),
13 => Some(Self::InvalidInput),
14 => Some(Self::ConcurrentModification),
15 => Some(Self::PermissionDenied),
16 => Some(Self::Unauthenticated),
17 => Some(Self::ServiceUnavailable),
18 => Some(Self::Internal),
19 => Some(Self::InvalidTableState),
20 => Some(Self::TableSchemaValidationError),
21 => Some(Self::Throttled),
_ => None,
}
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
Self::Unsupported => "Unsupported",
Self::NamespaceNotFound => "NamespaceNotFound",
Self::NamespaceAlreadyExists => "NamespaceAlreadyExists",
Self::NamespaceNotEmpty => "NamespaceNotEmpty",
Self::TableNotFound => "TableNotFound",
Self::TableAlreadyExists => "TableAlreadyExists",
Self::TableIndexNotFound => "TableIndexNotFound",
Self::TableIndexAlreadyExists => "TableIndexAlreadyExists",
Self::TableTagNotFound => "TableTagNotFound",
Self::TableTagAlreadyExists => "TableTagAlreadyExists",
Self::TransactionNotFound => "TransactionNotFound",
Self::TableVersionNotFound => "TableVersionNotFound",
Self::TableColumnNotFound => "TableColumnNotFound",
Self::InvalidInput => "InvalidInput",
Self::ConcurrentModification => "ConcurrentModification",
Self::PermissionDenied => "PermissionDenied",
Self::Unauthenticated => "Unauthenticated",
Self::ServiceUnavailable => "ServiceUnavailable",
Self::Internal => "Internal",
Self::InvalidTableState => "InvalidTableState",
Self::TableSchemaValidationError => "TableSchemaValidationError",
Self::Throttled => "Throttled",
};
write!(f, "{}", name)
}
}
#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
pub enum NamespaceError {
#[snafu(display("Unsupported: {message}"))]
Unsupported { message: String },
#[snafu(display("Namespace not found: {message}"))]
NamespaceNotFound { message: String },
#[snafu(display("Namespace already exists: {message}"))]
NamespaceAlreadyExists { message: String },
#[snafu(display("Namespace not empty: {message}"))]
NamespaceNotEmpty { message: String },
#[snafu(display("Table not found: {message}"))]
TableNotFound { message: String },
#[snafu(display("Table already exists: {message}"))]
TableAlreadyExists { message: String },
#[snafu(display("Table index not found: {message}"))]
TableIndexNotFound { message: String },
#[snafu(display("Table index already exists: {message}"))]
TableIndexAlreadyExists { message: String },
#[snafu(display("Table tag not found: {message}"))]
TableTagNotFound { message: String },
#[snafu(display("Table tag already exists: {message}"))]
TableTagAlreadyExists { message: String },
#[snafu(display("Transaction not found: {message}"))]
TransactionNotFound { message: String },
#[snafu(display("Table version not found: {message}"))]
TableVersionNotFound { message: String },
#[snafu(display("Table column not found: {message}"))]
TableColumnNotFound { message: String },
#[snafu(display("Invalid input: {message}"))]
InvalidInput { message: String },
#[snafu(display("Concurrent modification: {message}"))]
ConcurrentModification { message: String },
#[snafu(display("Permission denied: {message}"))]
PermissionDenied { message: String },
#[snafu(display("Unauthenticated: {message}"))]
Unauthenticated { message: String },
#[snafu(display("Service unavailable: {message}"))]
ServiceUnavailable { message: String },
#[snafu(display("Internal error: {message}"))]
Internal { message: String },
#[snafu(display("Invalid table state: {message}"))]
InvalidTableState { message: String },
#[snafu(display("Table schema validation error: {message}"))]
TableSchemaValidationError { message: String },
#[snafu(display("Throttled: {message}"))]
Throttled { message: String },
}
impl NamespaceError {
pub fn code(&self) -> ErrorCode {
match self {
Self::Unsupported { .. } => ErrorCode::Unsupported,
Self::NamespaceNotFound { .. } => ErrorCode::NamespaceNotFound,
Self::NamespaceAlreadyExists { .. } => ErrorCode::NamespaceAlreadyExists,
Self::NamespaceNotEmpty { .. } => ErrorCode::NamespaceNotEmpty,
Self::TableNotFound { .. } => ErrorCode::TableNotFound,
Self::TableAlreadyExists { .. } => ErrorCode::TableAlreadyExists,
Self::TableIndexNotFound { .. } => ErrorCode::TableIndexNotFound,
Self::TableIndexAlreadyExists { .. } => ErrorCode::TableIndexAlreadyExists,
Self::TableTagNotFound { .. } => ErrorCode::TableTagNotFound,
Self::TableTagAlreadyExists { .. } => ErrorCode::TableTagAlreadyExists,
Self::TransactionNotFound { .. } => ErrorCode::TransactionNotFound,
Self::TableVersionNotFound { .. } => ErrorCode::TableVersionNotFound,
Self::TableColumnNotFound { .. } => ErrorCode::TableColumnNotFound,
Self::InvalidInput { .. } => ErrorCode::InvalidInput,
Self::ConcurrentModification { .. } => ErrorCode::ConcurrentModification,
Self::PermissionDenied { .. } => ErrorCode::PermissionDenied,
Self::Unauthenticated { .. } => ErrorCode::Unauthenticated,
Self::ServiceUnavailable { .. } => ErrorCode::ServiceUnavailable,
Self::Internal { .. } => ErrorCode::Internal,
Self::InvalidTableState { .. } => ErrorCode::InvalidTableState,
Self::TableSchemaValidationError { .. } => ErrorCode::TableSchemaValidationError,
Self::Throttled { .. } => ErrorCode::Throttled,
}
}
pub fn from_code(code: u32, message: impl Into<String>) -> Self {
let message = message.into();
match ErrorCode::from_u32(code) {
Some(ErrorCode::Unsupported) => Self::Unsupported { message },
Some(ErrorCode::NamespaceNotFound) => Self::NamespaceNotFound { message },
Some(ErrorCode::NamespaceAlreadyExists) => Self::NamespaceAlreadyExists { message },
Some(ErrorCode::NamespaceNotEmpty) => Self::NamespaceNotEmpty { message },
Some(ErrorCode::TableNotFound) => Self::TableNotFound { message },
Some(ErrorCode::TableAlreadyExists) => Self::TableAlreadyExists { message },
Some(ErrorCode::TableIndexNotFound) => Self::TableIndexNotFound { message },
Some(ErrorCode::TableIndexAlreadyExists) => Self::TableIndexAlreadyExists { message },
Some(ErrorCode::TableTagNotFound) => Self::TableTagNotFound { message },
Some(ErrorCode::TableTagAlreadyExists) => Self::TableTagAlreadyExists { message },
Some(ErrorCode::TransactionNotFound) => Self::TransactionNotFound { message },
Some(ErrorCode::TableVersionNotFound) => Self::TableVersionNotFound { message },
Some(ErrorCode::TableColumnNotFound) => Self::TableColumnNotFound { message },
Some(ErrorCode::InvalidInput) => Self::InvalidInput { message },
Some(ErrorCode::ConcurrentModification) => Self::ConcurrentModification { message },
Some(ErrorCode::PermissionDenied) => Self::PermissionDenied { message },
Some(ErrorCode::Unauthenticated) => Self::Unauthenticated { message },
Some(ErrorCode::ServiceUnavailable) => Self::ServiceUnavailable { message },
Some(ErrorCode::Internal) => Self::Internal { message },
Some(ErrorCode::InvalidTableState) => Self::InvalidTableState { message },
Some(ErrorCode::TableSchemaValidationError) => {
Self::TableSchemaValidationError { message }
}
Some(ErrorCode::Throttled) => Self::Throttled { message },
None => Self::Internal { message },
}
}
}
impl From<NamespaceError> for lance_core::Error {
#[track_caller]
fn from(err: NamespaceError) -> Self {
Self::namespace_source(Box::new(err))
}
}
pub type Result<T> = std::result::Result<T, NamespaceError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_code_roundtrip() {
for code in 0..=21 {
let error_code = ErrorCode::from_u32(code).unwrap();
assert_eq!(error_code.as_u32(), code);
}
}
#[test]
fn test_unknown_error_code() {
assert!(ErrorCode::from_u32(999).is_none());
}
#[test]
fn test_namespace_error_code() {
let err = NamespaceError::TableNotFound {
message: "test table".to_string(),
};
assert_eq!(err.code(), ErrorCode::TableNotFound);
assert_eq!(err.code().as_u32(), 4);
}
#[test]
fn test_from_code() {
let err = NamespaceError::from_code(4, "table not found");
assert_eq!(err.code(), ErrorCode::TableNotFound);
assert!(err.to_string().contains("table not found"));
}
#[test]
fn test_from_unknown_code() {
let err = NamespaceError::from_code(999, "unknown error");
assert_eq!(err.code(), ErrorCode::Internal);
}
#[test]
fn test_convert_to_lance_error() {
let ns_err = NamespaceError::TableNotFound {
message: "users".to_string(),
};
let lance_err: lance_core::Error = ns_err.into();
match &lance_err {
lance_core::Error::Namespace { source, .. } => {
let downcast = source.downcast_ref::<NamespaceError>();
assert!(downcast.is_some());
assert_eq!(downcast.unwrap().code(), ErrorCode::TableNotFound);
}
_ => panic!("Expected Namespace error"),
}
}
#[test]
fn test_error_display() {
let err = NamespaceError::TableNotFound {
message: "users".to_string(),
};
assert_eq!(err.to_string(), "Table not found: users");
}
}