use crate::derive::{Object, ObjectRef};
use crate::object::{self, Object, ObjectArc, ObjectRefCore};
use std::ffi::c_void;
use tvm_ffi_sys::TVMFFIBacktraceUpdateMode::kTVMFFIBacktraceUpdateModeAppend;
use tvm_ffi_sys::{
TVMFFIByteArray, TVMFFIErrorCell, TVMFFIErrorCreateWithCauseAndExtraContext,
TVMFFIErrorMoveFromRaised, TVMFFIErrorSetRaised, TVMFFIObjectHandle, TVMFFITypeIndex,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ErrorKind<'a>(&'a str);
impl<'a> ErrorKind<'a> {
pub fn as_str(&self) -> &str {
self.0
}
}
impl<'a> std::fmt::Display for ErrorKind<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
pub const VALUE_ERROR: ErrorKind = ErrorKind("ValueError");
pub const TYPE_ERROR: ErrorKind = ErrorKind("TypeError");
pub const RUNTIME_ERROR: ErrorKind = ErrorKind("RuntimeError");
pub const ATTRIBUTE_ERROR: ErrorKind = ErrorKind("AttributeError");
pub const KEY_ERROR: ErrorKind = ErrorKind("KeyError");
pub const INDEX_ERROR: ErrorKind = ErrorKind("IndexError");
pub const INTERNAL_ERROR: ErrorKind = ErrorKind("InternalError");
pub const OVERFLOW_ERROR: ErrorKind = ErrorKind("OverflowError");
pub const ZERO_DIVISION_ERROR: ErrorKind = ErrorKind("ZeroDivisionError");
#[repr(C)]
#[derive(Object)]
#[type_key = "ffi.Error"]
#[type_index(TVMFFITypeIndex::kTVMFFIError)]
pub struct ErrorObj {
object: Object,
cell: TVMFFIErrorCell,
}
#[derive(Clone, ObjectRef)]
pub struct Error {
data: ObjectArc<ErrorObj>,
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
impl Error {
pub fn new(kind: ErrorKind<'_>, message: &str, traceback: &str) -> Self {
Self::new_with_cause_and_extra_context(kind, message, traceback, None, None)
}
pub fn new_with_cause_and_extra_context(
kind: ErrorKind<'_>,
message: &str,
traceback: &str,
cause_chain: Option<&Error>,
extra_context: Option<&object::ObjectRef>,
) -> Self {
unsafe {
let kind_data = TVMFFIByteArray::from_str(kind.as_str());
let message_data = TVMFFIByteArray::from_str(message);
let traceback_data = TVMFFIByteArray::from_str(traceback);
let mut error_handle: TVMFFIObjectHandle = std::ptr::null_mut();
let ret = TVMFFIErrorCreateWithCauseAndExtraContext(
&kind_data,
&message_data,
&traceback_data,
cause_chain.map_or(std::ptr::null_mut(), |cause| {
ObjectArc::as_raw(&cause.data) as TVMFFIObjectHandle
}),
extra_context.map_or(std::ptr::null_mut(), |context| {
ObjectArc::as_raw(object::ObjectRef::data(context)) as TVMFFIObjectHandle
}),
&mut error_handle,
);
assert_eq!(ret, 0, "Failed to create error object");
let error_obj = ObjectArc::from_raw(error_handle as *const ErrorObj);
Self { data: error_obj }
}
}
pub fn from_raised() -> Self {
unsafe {
let mut error_handle: TVMFFIObjectHandle = std::ptr::null_mut();
TVMFFIErrorMoveFromRaised(&mut error_handle as *mut TVMFFIObjectHandle);
assert!(
!error_handle.is_null(),
"Calling Error::from_raised but no error was raised"
);
let error_obj = ObjectArc::from_raw(error_handle as *const ErrorObj);
Self { data: error_obj }
}
}
pub fn set_raised(error: &Self) {
unsafe {
TVMFFIErrorSetRaised(ObjectArc::as_raw(&error.data) as TVMFFIObjectHandle);
}
}
pub fn kind(&self) -> ErrorKind<'_> {
ErrorKind(&self.data.cell.kind.as_str())
}
pub fn message(&self) -> &str {
self.data.cell.message.as_str()
}
pub fn backtrace(&self) -> &str {
self.data.cell.backtrace.as_str()
}
pub fn cause_chain(&self) -> Option<Error> {
let handle = self.data.cell.cause_chain;
if handle.is_null() {
return None;
}
unsafe {
object::unsafe_::inc_ref(handle.cast());
Some(Self {
data: ObjectArc::from_raw(handle.cast()),
})
}
}
pub fn extra_context(&self) -> Option<object::ObjectRef> {
let handle = self.data.cell.extra_context;
if handle.is_null() {
return None;
}
unsafe {
object::unsafe_::inc_ref(handle.cast());
Some(object::ObjectRef::from_data(ObjectArc::from_raw(
handle.cast(),
)))
}
}
pub fn traceback_most_recent_call_last(&self) -> String {
let backtrace = self.backtrace();
let backtrace_lines = backtrace.split('\n');
let mut traceback = String::new();
for line in backtrace_lines.rev() {
traceback.push_str(line);
traceback.push('\n');
}
traceback
}
pub fn with_appended_backtrace(this: Self, backtrace: &str) -> Self {
if ObjectArc::strong_count(&this.data) == 1 {
unsafe {
let backtrace_data = TVMFFIByteArray::from_str(backtrace);
(this.data.cell.update_backtrace)(
ObjectArc::as_raw(&this.data) as *mut ErrorObj as *mut c_void,
&backtrace_data,
kTVMFFIBacktraceUpdateModeAppend as i32,
);
this
}
} else {
let mut new_backtrace = String::new();
new_backtrace.push_str(this.backtrace());
new_backtrace.push_str(backtrace);
Self::new_with_cause_and_extra_context(
this.kind(),
this.message(),
&new_backtrace,
this.cause_chain().as_ref(),
this.extra_context().as_ref(),
)
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Traceback (most recent call last):\n{}{}: {}",
self.traceback_most_recent_call_last(),
self.kind().as_str(),
self.message()
)
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::error::Error for Error {}