mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-04-04 10:33:43 +00:00
* chore(nim-bindings): replace dynlib dlopen with plain importc
The dynlib pragma hard-coded a library path and resolved it via dlopen() at
runtime, preventing static linking and forcing a specific load-time path.
Using bare {.importc.} lets consumers choose: link liblibchat dynamically
at link time (--passL:-llibchat) or link it statically into their binary.
* Rust -> Nim ABI (#62)
* Use correct build hook
* force sret like return from rust code for nim compatibility
* Fix target mismatch
* Update usages
* ci: add nim-bindings-test
* fix(nim-bindings): fix ABI mismatch in destroy_* FFI functions and add defer-based cleanup
Nim's C backend silently transforms large struct parameters (>16 bytes) into
pointer parameters when calling importc functions. The destroy_* functions were
declared taking T by value in Rust, but Nim always passed &T — causing Rust to
read garbage from the stack on x86-64 (SIGILL on CI) while accidentally working
on ARM64 macOS due to that ABI coincidentally also using pointers for large structs.
Fix by changing all destroy_* functions to take &mut T and using drop_in_place,
which is the correct idiom for dropping a value through a pointer.
On the Nim side, replace scattered manual destroy calls with defer, which
guarantees cleanup on all exit paths and prevents use-after-destroy bugs.
---------
Co-authored-by: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com>
67 lines
1.8 KiB
Rust
67 lines
1.8 KiB
Rust
mod api;
|
|
mod context;
|
|
mod conversation;
|
|
mod crypto;
|
|
mod errors;
|
|
mod identity;
|
|
mod inbox;
|
|
mod proto;
|
|
mod types;
|
|
mod utils;
|
|
|
|
pub use api::*;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_ffi() {}
|
|
|
|
#[test]
|
|
fn test_message_roundtrip() {
|
|
let mut saro = create_context("saro".into());
|
|
let mut raya = create_context("raya".into());
|
|
|
|
// Raya Creates Bundle and Sends to Saro
|
|
let mut intro_result = CreateIntroResult {
|
|
error_code: -99,
|
|
intro_bytes: safer_ffi::Vec::EMPTY,
|
|
};
|
|
create_intro_bundle(&mut raya, &mut intro_result);
|
|
assert!(is_ok(intro_result.error_code));
|
|
|
|
let raya_bundle = intro_result.intro_bytes.as_ref();
|
|
|
|
// Saro creates a new conversation with Raya
|
|
let content: &[u8] = "hello".as_bytes();
|
|
|
|
let mut convo_result = NewConvoResult {
|
|
error_code: -99,
|
|
convo_id: "".into(),
|
|
payloads: safer_ffi::Vec::EMPTY,
|
|
};
|
|
create_new_private_convo(&mut saro, raya_bundle, content.into(), &mut convo_result);
|
|
assert!(is_ok(convo_result.error_code));
|
|
|
|
// Raya recieves initial message
|
|
let payload = convo_result.payloads.first().unwrap();
|
|
|
|
let mut handle_result: HandlePayloadResult = HandlePayloadResult {
|
|
error_code: -99,
|
|
convo_id: "".into(),
|
|
content: safer_ffi::Vec::EMPTY,
|
|
is_new_convo: false,
|
|
};
|
|
handle_payload(&mut raya, payload.data.as_ref(), &mut handle_result);
|
|
assert!(is_ok(handle_result.error_code));
|
|
|
|
// Check that the Content sent was the content received
|
|
assert!(handle_result.content.as_ref().as_slice() == content);
|
|
|
|
destroy_context(saro);
|
|
destroy_context(raya);
|
|
}
|
|
}
|