use thiserror::Error as ThisError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum ErrorCategory {
Validation = 0,
NotFound = 1,
Conflict = 2,
Unsupported = 3,
SdkFailure = 4,
Internal = 5,
}
#[derive(Debug, Clone, ThisError)]
#[error("[{category:?}] {message}")]
pub struct Error {
pub category: ErrorCategory,
pub code: i32,
pub message: String,
pub context: String,
}
impl Error {
pub fn validation(msg: impl Into<String>) -> Self {
Self {
category: ErrorCategory::Validation,
code: 0,
message: msg.into(),
context: String::new(),
}
}
pub fn not_found(msg: impl Into<String>) -> Self {
Self {
category: ErrorCategory::NotFound,
code: 0,
message: msg.into(),
context: String::new(),
}
}
pub fn conflict(msg: impl Into<String>) -> Self {
Self {
category: ErrorCategory::Conflict,
code: 0,
message: msg.into(),
context: String::new(),
}
}
pub fn unsupported(msg: impl Into<String>) -> Self {
Self {
category: ErrorCategory::Unsupported,
code: 0,
message: msg.into(),
context: String::new(),
}
}
pub fn sdk(msg: impl Into<String>) -> Self {
Self {
category: ErrorCategory::SdkFailure,
code: 0,
message: msg.into(),
context: String::new(),
}
}
pub fn internal(msg: impl Into<String>) -> Self {
Self {
category: ErrorCategory::Internal,
code: 0,
message: msg.into(),
context: String::new(),
}
}
pub fn with_context(mut self, ctx: impl Into<String>) -> Self {
self.context = ctx.into();
self
}
pub fn with_code(mut self, code: i32) -> Self {
self.code = code;
self
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub type Status = std::result::Result<(), Error>;
pub fn last_error() -> Option<Error> {
unsafe {
let cat = idax_sys::idax_last_error_category();
if cat < 0 {
return None;
}
let code = idax_sys::idax_last_error_code();
let msg_ptr = idax_sys::idax_last_error_message();
let message = if msg_ptr.is_null() {
String::new()
} else {
std::ffi::CStr::from_ptr(msg_ptr)
.to_string_lossy()
.into_owned()
};
let category = match cat {
0 => ErrorCategory::Validation,
1 => ErrorCategory::NotFound,
2 => ErrorCategory::Conflict,
3 => ErrorCategory::Unsupported,
4 => ErrorCategory::SdkFailure,
5 => ErrorCategory::Internal,
_ => ErrorCategory::Internal,
};
Some(Error {
category,
code,
message,
context: String::new(),
})
}
}
pub(crate) fn consume_last_error(fallback_msg: &str) -> Error {
last_error().unwrap_or_else(|| Error::sdk(fallback_msg))
}
#[allow(dead_code)]
pub(crate) fn bool_to_status(ok: bool, fallback_msg: &str) -> Status {
if ok {
Ok(())
} else {
Err(consume_last_error(fallback_msg))
}
}
pub(crate) fn int_to_status(ret: i32, fallback_msg: &str) -> Status {
if ret == 0 {
Ok(())
} else {
Err(consume_last_error(fallback_msg))
}
}
pub(crate) unsafe fn cstr_to_string(
ptr: *const std::ffi::c_char,
fallback_msg: &str,
) -> Result<String> {
if ptr.is_null() {
Err(consume_last_error(fallback_msg))
} else {
unsafe { Ok(std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned()) }
}
}
pub(crate) unsafe fn cstr_to_string_free(
ptr: *mut std::ffi::c_char,
fallback_msg: &str,
) -> Result<String> {
if ptr.is_null() {
Err(consume_last_error(fallback_msg))
} else {
unsafe {
let s = std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned();
idax_sys::idax_free_string(ptr);
Ok(s)
}
}
}
pub(crate) unsafe fn consume_c_string(ptr: *mut std::ffi::c_char) -> String {
if ptr.is_null() {
String::new()
} else {
unsafe {
let s = std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned();
idax_sys::idax_free_string(ptr);
s
}
}
}