#[macro_export]
macro_rules! c_try {
($body:expr) => {{
let __result: $crate::error::RwResult<_> = $crate::error::catch_panic(|| $body);
match __result {
Ok(val) => val,
Err(e) => {
log::error!("[rust_widgets] C ABI error: {e}");
$crate::error::ffi::record_last_ffi_error(e.clone());
$crate::error::c_try_fallback(e)
}
}
}};
}
pub fn c_try_fallback<T>(_e: super::RwError) -> T
where
T: CAbiSafe,
{
T::c_abi_fallback()
}
static LAST_FFI_ERROR: crate::compat::Mutex<Option<super::RwError>> =
crate::compat::Mutex::new(None);
pub fn record_last_ffi_error(error: super::RwError) {
let mut slot = crate::compat::lock(&LAST_FFI_ERROR);
*slot = Some(error);
}
#[cfg(all(any(feature = "desktop", feature = "jni", feature = "mobile-api"), not(alloc_frugal)))]
pub(crate) fn last_ffi_error() -> Option<super::RwError> {
LAST_FFI_ERROR.lock().ok().and_then(|slot| slot.clone())
}
pub fn clear_last_ffi_error() {
let mut slot = crate::compat::lock(&LAST_FFI_ERROR);
*slot = None;
}
pub fn record_capability_error(error: crate::widget::capability::types::CapabilityAccessError) {
use crate::widget::capability::types::CapabilityAccessError;
let (id, message) = match error {
CapabilityAccessError::UnknownWidget => {
(super::ErrorId::INVALID_ARGUMENT, "no widget is registered under that id")
}
CapabilityAccessError::UnknownProperty => (
super::ErrorId::INVALID_ARGUMENT,
"the widget does not publish a property by that name",
),
CapabilityAccessError::ReadOnlyProperty => {
(super::ErrorId::INVALID_ARGUMENT, "the property is read-only")
}
CapabilityAccessError::TypeMismatch => {
(super::ErrorId::INVALID_ARGUMENT, "the property does not accept that value kind")
}
CapabilityAccessError::UnsupportedOnWidget => {
(super::ErrorId::INVALID_ARGUMENT, "the property is not meaningful for this widget")
}
CapabilityAccessError::OutOfRange => {
(super::ErrorId::INVALID_ARGUMENT, "the value is out of range for this property")
}
CapabilityAccessError::UnknownCommand => {
(super::ErrorId::INVALID_ARGUMENT, "the widget has no command by that name")
}
};
record_last_ffi_error(super::RwError::new(id, message));
}
pub fn record_message_error(message: &str) {
use crate::compat::MiniToString;
let owned: &'static str = alloc::boxed::Box::leak(message.to_string().into_boxed_str());
record_last_ffi_error(super::RwError::new(super::ErrorId::INVALID_ARGUMENT, owned));
}
pub trait CAbiSafe {
fn c_abi_fallback() -> Self;
}
impl CAbiSafe for u64 {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for i64 {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for u32 {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for i32 {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for u16 {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for i16 {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for u8 {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for i8 {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for f32 {
fn c_abi_fallback() -> Self {
0.0
}
}
impl CAbiSafe for f64 {
fn c_abi_fallback() -> Self {
0.0
}
}
impl CAbiSafe for bool {
fn c_abi_fallback() -> Self {
false
}
}
impl CAbiSafe for *const core::ffi::c_char {
fn c_abi_fallback() -> Self {
core::ptr::null()
}
}
impl CAbiSafe for *mut core::ffi::c_char {
fn c_abi_fallback() -> Self {
core::ptr::null_mut()
}
}
impl CAbiSafe for *const u64 {
fn c_abi_fallback() -> Self {
core::ptr::null()
}
}
impl CAbiSafe for *mut u64 {
fn c_abi_fallback() -> Self {
core::ptr::null_mut()
}
}
impl CAbiSafe for usize {
fn c_abi_fallback() -> Self {
0
}
}
impl CAbiSafe for isize {
fn c_abi_fallback() -> Self {
0
}
}
#[macro_export]
macro_rules! c_try_void {
($body:expr) => {{
let __result: $crate::error::RwResult<_> = $crate::error::catch_panic(|| $body);
if let Err(e) = __result {
log::error!("[rust_widgets] C ABI error: {e}");
$crate::error::ffi::record_last_ffi_error(e.clone());
}
}};
}
#[cfg(all(test, feature = "desktop", not(alloc_frugal)))]
mod tests {
use super::*;
use crate::error::{ErrorId, RwError};
#[test]
fn last_ffi_error_roundtrip() {
clear_last_ffi_error();
assert!(last_ffi_error().is_none());
record_last_ffi_error(RwError::new(ErrorId::INVALID_ARGUMENT, "bad widget"));
let recorded = last_ffi_error().expect("error should be recorded");
assert_eq!(recorded.id, ErrorId::INVALID_ARGUMENT);
assert!(recorded.message.contains("bad widget"));
clear_last_ffi_error();
assert!(last_ffi_error().is_none());
}
}