From 4af031c421174c0f47171ef3b8aa73aefbf7268b Mon Sep 17 00:00:00 2001 From: Ivan FB Date: Sun, 31 May 2026 18:34:30 +0200 Subject: [PATCH] fix(codegen): Rust bindings target the *_cbor request exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Rust wrapper speaks CBOR, but after the native/CBOR split it still declared and called the bare `` request symbols — which are now the *native* (typed-args) entry points, so every Rust request hit the wrong ABI (struct/ptr mismatch). This is the Rust counterpart of the C++ fix (914c70a), which was missed at the time. Point the ffi.rs externs and the api.rs ctor/method calls at `_cbor`; the destructor has no CBOR variant and the event registration is unchanged here. Verified at runtime: the rust_client now creates a context and round-trips version / echo / schedule over CBOR. Co-Authored-By: Claude Opus 4.8 --- examples/timer/rust_bindings/src/api.rs | 20 ++++++++++---------- examples/timer/rust_bindings/src/ffi.rs | 10 +++++----- ffi/codegen/rust.nim | 15 +++++++++------ 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/examples/timer/rust_bindings/src/api.rs b/examples/timer/rust_bindings/src/api.rs index d8d9721..2afa582 100644 --- a/examples/timer/rust_bindings/src/api.rs +++ b/examples/timer/rust_bindings/src/api.rs @@ -191,7 +191,7 @@ impl MyTimerCtx { let req = MyTimerCreateCtorReq { config }; let req_bytes = encode_cbor(&req)?; let raw_bytes = ffi_call_sync(timeout, |cb, ud| unsafe { - let _ = ffi::my_timer_create(req_bytes.as_ptr(), req_bytes.len(), cb, ud); + let _ = ffi::my_timer_create_cbor(req_bytes.as_ptr(), req_bytes.len(), cb, ud); 0 })?; let addr_str: String = decode_cbor(&raw_bytes)?; @@ -203,7 +203,7 @@ impl MyTimerCtx { let req = MyTimerCreateCtorReq { config }; let req_bytes = encode_cbor(&req)?; let raw_bytes = ffi_call_async(timeout, move |cb, ud| unsafe { - let _ = ffi::my_timer_create(req_bytes.as_ptr(), req_bytes.len(), cb, ud); + let _ = ffi::my_timer_create_cbor(req_bytes.as_ptr(), req_bytes.len(), cb, ud); 0 }).await?; let addr_str: String = decode_cbor(&raw_bytes)?; @@ -265,7 +265,7 @@ impl MyTimerCtx { let req = MyTimerEchoReq { req }; let req_bytes = encode_cbor(&req)?; let raw_bytes = ffi_call_sync(self.timeout, |cb, ud| unsafe { - ffi::my_timer_echo(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len()) + ffi::my_timer_echo_cbor(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len()) })?; decode_cbor::(&raw_bytes) } @@ -275,7 +275,7 @@ impl MyTimerCtx { let req_bytes = encode_cbor(&req)?; let ptr = self.ptr as usize; let raw_bytes = ffi_call_async(self.timeout, move |cb, ud| unsafe { - ffi::my_timer_echo(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len()) + ffi::my_timer_echo_cbor(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len()) }).await?; decode_cbor::(&raw_bytes) } @@ -284,7 +284,7 @@ impl MyTimerCtx { let req = MyTimerVersionReq {}; let req_bytes = encode_cbor(&req)?; let raw_bytes = ffi_call_sync(self.timeout, |cb, ud| unsafe { - ffi::my_timer_version(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len()) + ffi::my_timer_version_cbor(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len()) })?; decode_cbor::(&raw_bytes) } @@ -294,7 +294,7 @@ impl MyTimerCtx { let req_bytes = encode_cbor(&req)?; let ptr = self.ptr as usize; let raw_bytes = ffi_call_async(self.timeout, move |cb, ud| unsafe { - ffi::my_timer_version(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len()) + ffi::my_timer_version_cbor(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len()) }).await?; decode_cbor::(&raw_bytes) } @@ -303,7 +303,7 @@ impl MyTimerCtx { let req = MyTimerComplexReq { req }; let req_bytes = encode_cbor(&req)?; let raw_bytes = ffi_call_sync(self.timeout, |cb, ud| unsafe { - ffi::my_timer_complex(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len()) + ffi::my_timer_complex_cbor(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len()) })?; decode_cbor::(&raw_bytes) } @@ -313,7 +313,7 @@ impl MyTimerCtx { let req_bytes = encode_cbor(&req)?; let ptr = self.ptr as usize; let raw_bytes = ffi_call_async(self.timeout, move |cb, ud| unsafe { - ffi::my_timer_complex(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len()) + ffi::my_timer_complex_cbor(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len()) }).await?; decode_cbor::(&raw_bytes) } @@ -322,7 +322,7 @@ impl MyTimerCtx { let req = MyTimerScheduleReq { job, retry, schedule }; let req_bytes = encode_cbor(&req)?; let raw_bytes = ffi_call_sync(self.timeout, |cb, ud| unsafe { - ffi::my_timer_schedule(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len()) + ffi::my_timer_schedule_cbor(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len()) })?; decode_cbor::(&raw_bytes) } @@ -332,7 +332,7 @@ impl MyTimerCtx { let req_bytes = encode_cbor(&req)?; let ptr = self.ptr as usize; let raw_bytes = ffi_call_async(self.timeout, move |cb, ud| unsafe { - ffi::my_timer_schedule(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len()) + ffi::my_timer_schedule_cbor(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len()) }).await?; decode_cbor::(&raw_bytes) } diff --git a/examples/timer/rust_bindings/src/ffi.rs b/examples/timer/rust_bindings/src/ffi.rs index 905acf4..ae893fc 100644 --- a/examples/timer/rust_bindings/src/ffi.rs +++ b/examples/timer/rust_bindings/src/ffi.rs @@ -9,11 +9,11 @@ pub type FFICallback = unsafe extern "C" fn( #[link(name = "my_timer")] extern "C" { - pub fn my_timer_create(req_cbor: *const u8, req_cbor_len: usize, callback: FFICallback, user_data: *mut c_void) -> *mut c_void; - pub fn my_timer_echo(ctx: *mut c_void, callback: FFICallback, user_data: *mut c_void, req_cbor: *const u8, req_cbor_len: usize) -> c_int; - pub fn my_timer_version(ctx: *mut c_void, callback: FFICallback, user_data: *mut c_void, req_cbor: *const u8, req_cbor_len: usize) -> c_int; - pub fn my_timer_complex(ctx: *mut c_void, callback: FFICallback, user_data: *mut c_void, req_cbor: *const u8, req_cbor_len: usize) -> c_int; - pub fn my_timer_schedule(ctx: *mut c_void, callback: FFICallback, user_data: *mut c_void, req_cbor: *const u8, req_cbor_len: usize) -> c_int; + pub fn my_timer_create_cbor(req_cbor: *const u8, req_cbor_len: usize, callback: FFICallback, user_data: *mut c_void) -> *mut c_void; + pub fn my_timer_echo_cbor(ctx: *mut c_void, callback: FFICallback, user_data: *mut c_void, req_cbor: *const u8, req_cbor_len: usize) -> c_int; + pub fn my_timer_version_cbor(ctx: *mut c_void, callback: FFICallback, user_data: *mut c_void, req_cbor: *const u8, req_cbor_len: usize) -> c_int; + pub fn my_timer_complex_cbor(ctx: *mut c_void, callback: FFICallback, user_data: *mut c_void, req_cbor: *const u8, req_cbor_len: usize) -> c_int; + pub fn my_timer_schedule_cbor(ctx: *mut c_void, callback: FFICallback, user_data: *mut c_void, req_cbor: *const u8, req_cbor_len: usize) -> c_int; pub fn my_timer_destroy(ctx: *mut c_void) -> c_int; pub fn my_timer_add_event_listener(ctx: *mut c_void, event_name: *const c_char, callback: FFICallback, user_data: *mut c_void) -> u64; pub fn my_timer_remove_event_listener(ctx: *mut c_void, listener_id: u64) -> c_int; diff --git a/ffi/codegen/rust.nim b/ffi/codegen/rust.nim index 4afbefb..b08d0be 100644 --- a/ffi/codegen/rust.nim +++ b/ffi/codegen/rust.nim @@ -195,15 +195,18 @@ proc generateFFIRs*(procs: seq[FFIProcMeta]): string = params.add("user_data: *mut c_void") params.add("req_cbor: *const u8") params.add("req_cbor_len: usize") - lines.add(" pub fn $1($2) -> c_int;" % [p.procName, params.join(", ")]) + # The Rust wrapper speaks CBOR, so it targets the `_cbor` exports; + # the bare `` symbol is the native (typed-args) entry point. + lines.add(" pub fn $1_cbor($2) -> c_int;" % [p.procName, params.join(", ")]) of FFIKind.CTOR: # Constructor: no ctx; returns the freshly-allocated handle params.add("req_cbor: *const u8") params.add("req_cbor_len: usize") params.add("callback: FFICallback") params.add("user_data: *mut c_void") - lines.add(" pub fn $1($2) -> *mut c_void;" % [p.procName, params.join(", ")]) + lines.add(" pub fn $1_cbor($2) -> *mut c_void;" % [p.procName, params.join(", ")]) of FFIKind.DTOR: + # The destructor takes no request payload, so it has no `_cbor` variant. params.add("ctx: *mut c_void") lines.add(" pub fn $1($2) -> c_int;" % [p.procName, params.join(", ")]) @@ -646,7 +649,7 @@ proc generateApiRs*( # on the callback. lines.add(" let raw_bytes = ffi_call_sync(timeout, |cb, ud| unsafe {") lines.add( - " let _ = ffi::$1(req_bytes.as_ptr(), req_bytes.len(), cb, ud);" % + " let _ = ffi::$1_cbor(req_bytes.as_ptr(), req_bytes.len(), cb, ud);" % [ctor.procName] ) lines.add(" 0") @@ -673,7 +676,7 @@ proc generateApiRs*( # and rely on the callback to deliver the ctx address. lines.add(" let raw_bytes = ffi_call_async(timeout, move |cb, ud| unsafe {") lines.add( - " let _ = ffi::$1(req_bytes.as_ptr(), req_bytes.len(), cb, ud);" % + " let _ = ffi::$1_cbor(req_bytes.as_ptr(), req_bytes.len(), cb, ud);" % [ctor.procName] ) lines.add(" 0") @@ -846,7 +849,7 @@ proc generateApiRs*( lines.add(" let req_bytes = encode_cbor(&req)?;") lines.add(" let raw_bytes = ffi_call_sync(self.timeout, |cb, ud| unsafe {") lines.add( - " ffi::$1(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len())" % + " ffi::$1_cbor(self.ptr, cb, ud, req_bytes.as_ptr(), req_bytes.len())" % [m.procName] ) lines.add(" })?;") @@ -868,7 +871,7 @@ proc generateApiRs*( " let raw_bytes = ffi_call_async(self.timeout, move |cb, ud| unsafe {" ) lines.add( - " ffi::$1(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len())" % + " ffi::$1_cbor(ptr as *mut c_void, cb, ud, req_bytes.as_ptr(), req_bytes.len())" % [m.procName] ) lines.add(" }).await?;")