use crate::errors::{catch_panic, FfiError, FfiResult};
use crate::memory::FfiBuffer;
pub struct BridgeCall {
input: FfiBuffer,
}
impl BridgeCall {
pub fn new(input: FfiBuffer) -> Self {
BridgeCall { input }
}
pub fn run<F>(self, handler: F) -> FfiResult
where
F: FnOnce(&FfiBuffer) -> Result<FfiBuffer, FfiError> + std::panic::UnwindSafe,
{
let input = self.input;
catch_panic(move || handler(&input))
}
pub fn run_json<I, O, F>(self, handler: F) -> FfiResult
where
I: serde::de::DeserializeOwned + std::panic::UnwindSafe,
O: serde::Serialize,
F: FnOnce(I) -> Result<O, FfiError> + std::panic::UnwindSafe,
{
let input = self.input;
catch_panic(move || {
let value: I = unsafe { input.to_json() }?;
let output = handler(value)?;
FfiBuffer::from_json(&output)
})
}
pub fn into_buffer(self) -> FfiBuffer {
self.input
}
}
#[no_mangle]
pub extern "C" fn ffi_echo(input: FfiBuffer) -> FfiResult {
BridgeCall::new(input).run(|buf| {
let bytes = unsafe { buf.as_slice() }.to_vec();
Ok(FfiBuffer::from_vec(bytes))
})
}
#[no_mangle]
pub extern "C" fn ffi_version() -> FfiBuffer {
FfiBuffer::from_vec(env!("CARGO_PKG_VERSION").as_bytes().to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::ffi_result_free;
use crate::memory::ffi_buffer_free;
#[test]
fn bridge_call_run_ok() {
let input = FfiBuffer::from_vec(b"ping".to_vec());
let result = BridgeCall::new(input).run(|buf| {
let bytes = unsafe { buf.as_slice() }.to_vec();
assert_eq!(&bytes, b"ping");
Ok(FfiBuffer::from_vec(b"pong".to_vec()))
});
assert!(result.is_ok());
let payload = unsafe { result.payload.as_slice() };
assert_eq!(payload, b"pong");
ffi_result_free(result);
}
#[test]
fn bridge_call_run_json() {
#[derive(serde::Serialize, serde::Deserialize)]
struct Req {
n: u32,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
struct Res {
doubled: u32,
}
let input = FfiBuffer::from_json(&Req { n: 21 }).unwrap();
let result = BridgeCall::new(input).run_json(|req: Req| Ok(Res { doubled: req.n * 2 }));
assert!(result.is_ok());
let decoded: Res = unsafe { result.payload.to_json() }.unwrap();
assert_eq!(decoded, Res { doubled: 42 });
ffi_result_free(result);
}
#[test]
fn ffi_echo_round_trip() {
let input = FfiBuffer::from_vec(b"hello echo".to_vec());
let result = ffi_echo(input);
assert!(result.is_ok());
let output = unsafe { result.payload.as_slice() };
assert_eq!(output, b"hello echo");
ffi_result_free(result);
}
#[test]
fn ffi_version_returns_version_string() {
let buf = ffi_version();
let s = unsafe { buf.as_slice() };
assert!(!s.is_empty());
ffi_buffer_free(buf);
}
}