use std::panic::{AssertUnwindSafe, catch_unwind};
use crate::{Error, Result, XabiOwnedBytes};
pub const ABI_VERSION: u32 = 1;
pub const OK: i32 = 0;
pub const ERR_PANIC: i32 = -1;
pub const ERR_EXPORT: i32 = -2;
pub const ERR_HOST: i32 = -3;
pub const ERR_INVALID_ARGUMENT: i32 = -4;
pub const POLL_READY: i32 = 0;
pub const POLL_PENDING: i32 = 1;
pub const CAP_NONE: u64 = 0;
pub fn catch_unwind_code(f: impl FnOnce() -> i32) -> i32 {
match catch_unwind(AssertUnwindSafe(f)) {
Ok(code) => code,
Err(_) => ERR_PANIC,
}
}
pub fn catch_unwind_owned(f: impl FnOnce() -> XabiOwnedBytes) -> XabiOwnedBytes {
match catch_unwind(AssertUnwindSafe(f)) {
Ok(value) => value,
Err(_) => XabiOwnedBytes::from_string("panic crossing xabi boundary".to_string()),
}
}
pub fn catch_unwind_or<T>(default: T, f: impl FnOnce() -> T) -> T {
match catch_unwind(AssertUnwindSafe(f)) {
Ok(value) => value,
Err(_) => default,
}
}
pub fn validate_size(actual: usize, expected: usize, name: &'static str) -> Result<()> {
if actual < expected {
return Err(Error::AbiMismatch(format!(
"{name} size {actual} is smaller than expected {expected}"
)));
}
Ok(())
}
pub fn validate_abi_version(actual: u32, expected: u32, name: &'static str) -> Result<()> {
if actual != expected {
return Err(Error::AbiMismatch(format!(
"{name} abi_version {actual} does not match expected {expected}"
)));
}
Ok(())
}
pub fn status_to_result(code: i32, context: &str) -> Result<()> {
match code {
OK => Ok(()),
ERR_PANIC => Err(Error::Export(format!(
"{context}: panic crossed xabi boundary"
))),
ERR_EXPORT => Err(Error::Export(format!(
"{context}: export returned an error"
))),
ERR_HOST => Err(Error::Export(format!(
"{context}: host callback returned an error"
))),
ERR_INVALID_ARGUMENT => Err(Error::Export(format!("{context}: invalid argument"))),
other => Err(Error::Export(format!(
"{context}: unknown xabi code {other}"
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_to_result_reports_context() {
let err = status_to_result(ERR_INVALID_ARGUMENT, "Xabi.method").unwrap_err();
assert_eq!(err.to_string(), "Xabi.method: invalid argument");
}
#[test]
fn catch_unwind_owned_returns_error_payload_on_panic() {
let owned = catch_unwind_owned(|| panic!("boom"));
let message = unsafe { owned.to_string_and_free() }.unwrap();
assert_eq!(message, "panic crossing xabi boundary");
}
}