1use crate::errors::{catch_panic, FfiError, FfiResult};
10use crate::memory::FfiBuffer;
11
12pub struct BridgeCall {
29 input: FfiBuffer,
30}
31
32impl BridgeCall {
33 pub fn new(input: FfiBuffer) -> Self {
35 BridgeCall { input }
36 }
37
38 pub fn run<F>(self, handler: F) -> FfiResult
43 where
44 F: FnOnce(&FfiBuffer) -> Result<FfiBuffer, FfiError> + std::panic::UnwindSafe,
45 {
46 let input = self.input;
47 catch_panic(move || handler(&input))
48 }
49
50 pub fn run_json<I, O, F>(self, handler: F) -> FfiResult
55 where
56 I: serde::de::DeserializeOwned + std::panic::UnwindSafe,
57 O: serde::Serialize,
58 F: FnOnce(I) -> Result<O, FfiError> + std::panic::UnwindSafe,
59 {
60 let input = self.input;
61 catch_panic(move || {
62 let value: I = unsafe { input.to_json() }?;
63 let output = handler(value)?;
64 FfiBuffer::from_json(&output)
65 })
66 }
67
68 pub fn into_buffer(self) -> FfiBuffer {
70 self.input
71 }
72}
73
74#[no_mangle]
80pub extern "C" fn ffi_echo(input: FfiBuffer) -> FfiResult {
81 BridgeCall::new(input).run(|buf| {
82 let bytes = unsafe { buf.as_slice() }.to_vec();
84 Ok(FfiBuffer::from_vec(bytes))
85 })
86}
87
88#[no_mangle]
92pub extern "C" fn ffi_version() -> FfiBuffer {
93 FfiBuffer::from_vec(env!("CARGO_PKG_VERSION").as_bytes().to_vec())
94}
95
96#[cfg(test)]
99mod tests {
100 use super::*;
101 use crate::errors::ffi_result_free;
102 use crate::memory::ffi_buffer_free;
103
104 #[test]
105 fn bridge_call_run_ok() {
106 let input = FfiBuffer::from_vec(b"ping".to_vec());
107 let result = BridgeCall::new(input).run(|buf| {
108 let bytes = unsafe { buf.as_slice() }.to_vec();
109 assert_eq!(&bytes, b"ping");
110 Ok(FfiBuffer::from_vec(b"pong".to_vec()))
111 });
112 assert!(result.is_ok());
113 let payload = unsafe { result.payload.as_slice() };
114 assert_eq!(payload, b"pong");
115 ffi_result_free(result);
116 }
117
118 #[test]
119 fn bridge_call_run_json() {
120 #[derive(serde::Serialize, serde::Deserialize)]
121 struct Req {
122 n: u32,
123 }
124 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
125 struct Res {
126 doubled: u32,
127 }
128
129 let input = FfiBuffer::from_json(&Req { n: 21 }).unwrap();
130 let result = BridgeCall::new(input).run_json(|req: Req| Ok(Res { doubled: req.n * 2 }));
131 assert!(result.is_ok());
132 let decoded: Res = unsafe { result.payload.to_json() }.unwrap();
133 assert_eq!(decoded, Res { doubled: 42 });
134 ffi_result_free(result);
135 }
136
137 #[test]
138 fn ffi_echo_round_trip() {
139 let input = FfiBuffer::from_vec(b"hello echo".to_vec());
140 let result = ffi_echo(input);
141 assert!(result.is_ok());
142 let output = unsafe { result.payload.as_slice() };
143 assert_eq!(output, b"hello echo");
144 ffi_result_free(result);
145 }
146
147 #[test]
148 fn ffi_version_returns_version_string() {
149 let buf = ffi_version();
150 let s = unsafe { buf.as_slice() };
151 assert!(!s.is_empty());
152 ffi_buffer_free(buf);
153 }
154}