use std::os::raw::c_char;
use std::{ffi::NulError, fmt::Display};
use arrow_schema::ArrowError;
use crate::constants;
pub type AdbcStatusCode = u8;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Status {
Ok,
Unknown,
NotImplemented,
NotFound,
AlreadyExists,
InvalidArguments,
InvalidState,
InvalidData,
Integrity,
Internal,
IO,
Cancelled,
Timeout,
Unauthenticated,
Unauthorized,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Error {
pub message: String,
pub status: Status,
pub vendor_code: i32,
pub sqlstate: [c_char; 5], pub details: Option<Vec<(String, Vec<u8>)>>,
}
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
pub fn with_message_and_status(message: impl Into<String>, status: Status) -> Self {
Self {
message: message.into(),
status,
vendor_code: 0,
sqlstate: [0; 5],
details: None,
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:?}: {} (sqlstate: {:?}, vendor_code: {})",
self.status, self.message, self.sqlstate, self.vendor_code
)
}
}
impl std::error::Error for Error {}
impl From<ArrowError> for Error {
fn from(value: ArrowError) -> Self {
Self {
message: value.to_string(),
status: Status::Internal,
vendor_code: 0,
sqlstate: [0; 5],
details: None,
}
}
}
impl From<NulError> for Error {
fn from(value: NulError) -> Self {
Self {
message: format!(
"Interior null byte was found at position {}",
value.nul_position()
),
status: Status::InvalidData,
vendor_code: 0,
sqlstate: [0; 5],
details: None,
}
}
}
impl From<std::str::Utf8Error> for Error {
fn from(value: std::str::Utf8Error) -> Self {
Self {
message: format!("Error while decoding UTF-8: {value}"),
status: Status::Internal,
vendor_code: 0,
sqlstate: [0; 5],
details: None,
}
}
}
impl From<std::ffi::IntoStringError> for Error {
fn from(value: std::ffi::IntoStringError) -> Self {
let error = value.utf8_error();
error.into()
}
}
impl TryFrom<AdbcStatusCode> for Status {
type Error = Error;
fn try_from(value: AdbcStatusCode) -> Result<Self> {
match value {
constants::ADBC_STATUS_OK => Ok(Status::Ok),
constants::ADBC_STATUS_UNKNOWN => Ok(Status::Unknown),
constants::ADBC_STATUS_NOT_IMPLEMENTED => Ok(Status::NotImplemented),
constants::ADBC_STATUS_NOT_FOUND => Ok(Status::NotFound),
constants::ADBC_STATUS_ALREADY_EXISTS => Ok(Status::AlreadyExists),
constants::ADBC_STATUS_INVALID_ARGUMENT => Ok(Status::InvalidArguments),
constants::ADBC_STATUS_INVALID_STATE => Ok(Status::InvalidState),
constants::ADBC_STATUS_INVALID_DATA => Ok(Status::InvalidData),
constants::ADBC_STATUS_INTEGRITY => Ok(Status::Integrity),
constants::ADBC_STATUS_INTERNAL => Ok(Status::Internal),
constants::ADBC_STATUS_IO => Ok(Status::IO),
constants::ADBC_STATUS_CANCELLED => Ok(Status::Cancelled),
constants::ADBC_STATUS_TIMEOUT => Ok(Status::Timeout),
constants::ADBC_STATUS_UNAUTHENTICATED => Ok(Status::Unauthenticated),
constants::ADBC_STATUS_UNAUTHORIZED => Ok(Status::Unauthorized),
v => Err(Error::with_message_and_status(
format!("Unknown status code: {v}"),
Status::InvalidData,
)),
}
}
}
impl From<Status> for AdbcStatusCode {
fn from(value: Status) -> Self {
match value {
Status::Ok => constants::ADBC_STATUS_OK,
Status::Unknown => constants::ADBC_STATUS_UNKNOWN,
Status::NotImplemented => constants::ADBC_STATUS_NOT_IMPLEMENTED,
Status::NotFound => constants::ADBC_STATUS_NOT_FOUND,
Status::AlreadyExists => constants::ADBC_STATUS_ALREADY_EXISTS,
Status::InvalidArguments => constants::ADBC_STATUS_INVALID_ARGUMENT,
Status::InvalidState => constants::ADBC_STATUS_INVALID_STATE,
Status::InvalidData => constants::ADBC_STATUS_INVALID_DATA,
Status::Integrity => constants::ADBC_STATUS_INTEGRITY,
Status::Internal => constants::ADBC_STATUS_INTERNAL,
Status::IO => constants::ADBC_STATUS_IO,
Status::Cancelled => constants::ADBC_STATUS_CANCELLED,
Status::Timeout => constants::ADBC_STATUS_TIMEOUT,
Status::Unauthenticated => constants::ADBC_STATUS_UNAUTHENTICATED,
Status::Unauthorized => constants::ADBC_STATUS_UNAUTHORIZED,
}
}
}