use std::{
cell::RefCell,
os::raw::{c_char, c_int},
slice,
};
thread_local! {
static LAST_ERROR: RefCell<Option<String>> = const { RefCell::new(None) };
}
pub(crate) fn update_last_error(err: String) {
LAST_ERROR.with(|prev| {
*prev.borrow_mut() = Some(err);
});
}
fn take_last_error() -> Option<String> {
LAST_ERROR.with(|prev| prev.borrow_mut().take())
}
#[no_mangle]
pub extern "C" fn hb_last_error_length() -> c_int {
LAST_ERROR.with(|prev| match *prev.borrow() {
Some(ref err) => err.to_string().len() as c_int + 1,
None => 0,
})
}
macro_rules! ffi_error {
($result:expr) => {{
match $result {
Ok(r) => r,
Err(e) => {
update_last_error(e.to_string());
return 1;
}
}
}};
}
pub(crate) use ffi_error;
#[no_mangle]
pub unsafe extern "C" fn hb_last_error_message(buffer: *mut c_char, length: c_int) -> c_int {
if buffer.is_null() {
return -1;
}
let last_error = match take_last_error() {
Some(err) => err,
None => return 0,
};
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
let buffer = slice::from_raw_parts_mut(buffer, length as usize);
#[cfg(not(all(target_arch = "aarch64", target_os = "linux")))]
let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize);
if last_error.len() >= buffer.len() {
return -1;
}
std::ptr::copy_nonoverlapping(last_error.as_ptr(), buffer.as_mut_ptr(), last_error.len());
buffer[last_error.len()] = 0;
last_error.len() as c_int
}