use core::error::Error;
use core::fmt::Display;
use libffi_sys::{
ffi_status, ffi_status_FFI_BAD_ABI, ffi_status_FFI_BAD_ARGTYPE, ffi_status_FFI_BAD_TYPEDEF,
};
use crate::types::Type;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum LibffiError {
FfiBadTypeDef = ffi_status_FFI_BAD_TYPEDEF,
FfiBadAbi = ffi_status_FFI_BAD_ABI,
FfiBadArgType = ffi_status_FFI_BAD_ARGTYPE,
}
impl Display for LibffiError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
LibffiError::FfiBadTypeDef => write!(
f,
"Attempted to use a malformed `ffi_type`. This is most likely caused by an empty struct, which is not supported by libffi."
),
LibffiError::FfiBadAbi => {
write!(f, "Attempted to use an ABI not recognized by libffi.")
}
LibffiError::FfiBadArgType => write!(
f,
"Attempted to use an unsupported type for variadic arguments."
),
}
}
}
impl Error for LibffiError {}
impl LibffiError {
#[expect(
non_upper_case_globals,
reason = "Constant names from an external crate (libffi_sys)."
)]
pub fn from_status(status: ffi_status) -> Option<LibffiError> {
match status {
ffi_status_FFI_BAD_TYPEDEF => Some(LibffiError::FfiBadTypeDef),
ffi_status_FFI_BAD_ABI => Some(LibffiError::FfiBadAbi),
ffi_status_FFI_BAD_ARGTYPE => Some(LibffiError::FfiBadArgType),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EmptyStructError;
impl Display for EmptyStructError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Tried to create a struct `Type` without any members.")
}
}
impl Error for EmptyStructError {}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct InvalidVariadicTypeError(pub Type);
impl Display for InvalidVariadicTypeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"Tried to use `{:?}` as a variadic argument type, but it is not valid for variadic arguments.",
self.0
)
}
}
impl Error for InvalidVariadicTypeError {}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ClosureAllocationError;
impl Display for ClosureAllocationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Libffi was unable to allocate memory for the closure.")
}
}
impl Error for ClosureAllocationError {}
#[cfg(test)]
mod test {
use core::ptr::null_mut;
use libffi_sys::{
ffi_abi, ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_prep_cif, ffi_prep_cif_var, ffi_type,
ffi_type_enum_STRUCT,
};
use super::*;
use crate::raw::{ffi_type_sint8, ffi_type_void};
#[test]
fn no_error_on_ok() {
let mut cif = ffi_cif::default();
let status = unsafe {
ffi_prep_cif(
&raw mut cif,
ffi_abi_FFI_DEFAULT_ABI,
0,
&raw mut ffi_type_void,
null_mut(),
)
};
assert!(LibffiError::from_status(status).is_none());
}
#[test]
fn bad_abi_status() {
let mut cif = ffi_cif::default();
let status = unsafe {
ffi_prep_cif(
&raw mut cif,
ffi_abi::MAX,
0,
&raw mut ffi_type_void,
null_mut(),
)
};
assert_eq!(
LibffiError::from_status(status),
Some(LibffiError::FfiBadAbi)
);
}
#[test]
fn bad_typedef_status() {
let mut typedef_members = [null_mut()];
let mut bad_typedef = ffi_type {
type_: ffi_type_enum_STRUCT,
elements: typedef_members.as_mut_ptr(),
..Default::default()
};
let mut cif = ffi_cif::default();
let status = unsafe {
ffi_prep_cif(
&raw mut cif,
ffi_abi_FFI_DEFAULT_ABI,
0,
&raw mut bad_typedef,
null_mut(),
)
};
assert_eq!(
LibffiError::from_status(status),
Some(LibffiError::FfiBadTypeDef)
);
}
#[test]
fn bad_argtype_status() {
let mut arguments = [&raw mut ffi_type_sint8, &raw mut ffi_type_sint8];
let mut cif = ffi_cif::default();
let status = unsafe {
ffi_prep_cif_var(
&raw mut cif,
ffi_abi_FFI_DEFAULT_ABI,
1,
2,
&raw mut ffi_type_void,
arguments.as_mut_ptr(),
)
};
assert_eq!(
LibffiError::from_status(status),
Some(LibffiError::FfiBadArgType)
);
}
}