From 135347cdd05f5b3594610b336530183c511ff620 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 6 Feb 2026 23:58:23 +0700 Subject: [PATCH] Update Conversation handles over FFI (#46) * Remove convo handles * Remove convo_handle from docs --- conversations/src/api.rs | 14 ++++----- conversations/src/context.rs | 46 +++++++++--------------------- conversations/src/errors.rs | 4 +-- nim-bindings/examples/pingpong.nim | 2 +- nim-bindings/src/bindings.nim | 12 ++++++-- nim-bindings/src/libchat.nim | 8 +++--- 6 files changed, 37 insertions(+), 49 deletions(-) diff --git a/conversations/src/api.rs b/conversations/src/api.rs index dc1d215..d8951bd 100644 --- a/conversations/src/api.rs +++ b/conversations/src/api.rs @@ -12,7 +12,7 @@ pub enum ErrorCode { UnknownError = -6, } -use crate::context::{Context, ConvoHandle, Introduction}; +use crate::context::{Context, Introduction}; /// Opaque wrapper for Context #[derive_ReprC] @@ -74,13 +74,13 @@ pub fn create_new_private_convo( let Ok(intro) = Introduction::try_from(bundle.as_slice()) else { return NewConvoResult { error_code: ErrorCode::BadIntro as i32, - convo_id: 0, + convo_id: "".into(), payloads: Vec::new().into(), }; }; // Create conversation - let (convo_handle, payloads) = ctx.0.create_private_convo(&intro, &content); + let (convo_id, payloads) = ctx.0.create_private_convo(&intro, &content); // Convert payloads to FFI-compatible vector let ffi_payloads: Vec = payloads @@ -93,7 +93,7 @@ pub fn create_new_private_convo( NewConvoResult { error_code: 0, - convo_id: convo_handle, + convo_id: convo_id.to_string().into(), payloads: ffi_payloads.into(), } } @@ -106,10 +106,10 @@ pub fn create_new_private_convo( #[ffi_export] pub fn send_content( ctx: &mut ContextHandle, - convo_handle: ConvoHandle, + convo_id: repr_c::String, content: c_slice::Ref<'_, u8>, ) -> PayloadResult { - let payloads = match ctx.0.send_content(convo_handle, &content) { + let payloads = match ctx.0.send_content(&convo_id, &content) { Ok(p) => p, Err(_) => { return PayloadResult { @@ -203,7 +203,7 @@ pub fn destroy_payload_result(result: PayloadResult) { #[repr(C)] pub struct NewConvoResult { pub error_code: i32, - pub convo_id: u32, + pub convo_id: repr_c::String, pub payloads: repr_c::Vec, } diff --git a/conversations/src/context.rs b/conversations/src/context.rs index e19050f..1a0f86f 100644 --- a/conversations/src/context.rs +++ b/conversations/src/context.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, rc::Rc, sync::Arc}; +use std::rc::Rc; use crate::{ conversation::{ConversationId, ConversationStore, Convo, Id}, @@ -9,22 +9,15 @@ use crate::{ types::{AddressedEnvelope, ContentData}, }; +pub use crate::conversation::ConversationIdOwned; pub use crate::inbox::Introduction; -//Offset handles to make debuging easier -const INITIAL_CONVO_HANDLE: u32 = 0xF5000001; - -/// Used to identify a conversation on the othersize of the FFI. -pub type ConvoHandle = u32; - // This is the main entry point to the conversations api. // Ctx manages lifetimes of objects to process and generate payloads. pub struct Context { _identity: Rc, store: ConversationStore, inbox: Inbox, - convo_handle_map: HashMap>, - next_convo_handle: ConvoHandle, } impl Context { @@ -35,8 +28,6 @@ impl Context { _identity: identity, store: ConversationStore::new(), inbox, - convo_handle_map: HashMap::new(), - next_convo_handle: INITIAL_CONVO_HANDLE, } } @@ -44,7 +35,7 @@ impl Context { &mut self, remote_bundle: &Introduction, content: &[u8], - ) -> (ConvoHandle, Vec) { + ) -> (ConversationIdOwned, Vec) { let (convo, payloads) = self .inbox .invite_to_private_convo(remote_bundle, content) @@ -55,17 +46,17 @@ impl Context { .map(|p| p.to_envelope(convo.id().to_string())) .collect(); - let convo_handle = self.add_convo(Box::new(convo)); - (convo_handle, payload_bytes) + let convo_id = self.add_convo(Box::new(convo)); + (convo_id, payload_bytes) } pub fn send_content( &mut self, - convo_handle: ConvoHandle, + convo_id: ConversationId, content: &[u8], ) -> Result, ChatError> { - // Lookup convo from handle - let convo = self.get_convo_mut(convo_handle)?; + // Lookup convo by id + let convo = self.get_convo_mut(convo_id)?; // Generate encrypted payloads let payloads = convo.send_message(content)?; @@ -91,7 +82,7 @@ impl Context { match convo_id { c if c == self.inbox.id() => self.dispatch_to_inbox(enc), c if self.store.has(&c) => self.dispatch_to_convo(&c, enc), - _ => Err(ChatError::NoConvo(0)), // TODO: Remove ConvoHandle type + _ => Err(ChatError::NoConvo(convo_id)), } } @@ -123,26 +114,17 @@ impl Context { Ok(Introduction::from(pkb).into()) } - fn add_convo(&mut self, convo: Box) -> ConvoHandle { - let handle = self.next_convo_handle; - self.next_convo_handle += 1; + fn add_convo(&mut self, convo: Box) -> ConversationIdOwned { let convo_id = self.store.insert_convo(convo); - self.convo_handle_map.insert(handle, convo_id); - handle + convo_id } - // Returns a mutable reference to a Convo for a given ConvoHandle - fn get_convo_mut(&mut self, handle: ConvoHandle) -> Result<&mut dyn Convo, ChatError> { - let convo_id = self - .convo_handle_map - .get(&handle) - .ok_or_else(|| ChatError::NoConvo(handle))? - .clone(); - + // Returns a mutable reference to a Convo for a given ConvoId + fn get_convo_mut(&mut self, convo_id: ConversationId) -> Result<&mut dyn Convo, ChatError> { self.store .get_mut(&convo_id) - .ok_or_else(|| ChatError::NoConvo(handle)) + .ok_or_else(|| ChatError::NoConvo(convo_id.into())) } } diff --git a/conversations/src/errors.rs b/conversations/src/errors.rs index 06cd563..5b6b438 100644 --- a/conversations/src/errors.rs +++ b/conversations/src/errors.rs @@ -18,8 +18,8 @@ pub enum ChatError { InvalidKeyLength, #[error("bytes provided to {0} failed")] BadParsing(&'static str), - #[error("convo with handle: {0} was not found")] - NoConvo(u32), + #[error("convo with id: {0} was not found")] + NoConvo(String), } #[derive(Error, Debug)] diff --git a/nim-bindings/examples/pingpong.nim b/nim-bindings/examples/pingpong.nim index 0c23fb2..339e5e6 100644 --- a/nim-bindings/examples/pingpong.nim +++ b/nim-bindings/examples/pingpong.nim @@ -24,7 +24,7 @@ proc pingpong() = echo "Raya's Intro Bundle: ",intro var (convo_sr, payloads) = saro.createNewPrivateConvo(intro, encode("Hey Raya")).expect("[Saro] Couldn't create convo") - echo "ConvoHandle:: ", convo_sr + echo "ConvoId:: ", convo_sr echo "Payload:: ", payloads ## Send Payloads to Raya diff --git a/nim-bindings/src/bindings.nim b/nim-bindings/src/bindings.nim index 3edf2d9..c453e0a 100644 --- a/nim-bindings/src/bindings.nim +++ b/nim-bindings/src/bindings.nim @@ -36,7 +36,6 @@ const # Opaque handle type for Context type ContextHandle* = pointer -type ConvoHandle* = uint32 type ## Slice for passing byte arrays to safer_ffi functions @@ -77,7 +76,7 @@ type ## error_code is 0 on success, negative on error (see ErrorCode) NewConvoResult* = object error_code*: int32 - convo_id*: uint32 + convo_id*: ReprCString payloads*: VecPayload # FFI function imports @@ -112,7 +111,7 @@ proc create_new_private_convo*( ## The result must be freed with destroy_payload_result() proc send_content*( ctx: ContextHandle, - convo_handle: ConvoHandle, + convo_id: SliceUint8, content: SliceUint8, ): PayloadResult {.importc, dynlib: CONVERSATIONS_LIB.} @@ -173,3 +172,10 @@ proc `[]`*(v: VecPayload, i: int): Payload = ## Get length of VecPayload proc len*(v: VecPayload): int = int(v.len) + +## Convert a string to seq[byte] +proc toBytes*(s: string): seq[byte] = + if s.len == 0: + return @[] + result = newSeq[byte](s.len) + copyMem(addr result[0], unsafeAddr s[0], s.len) diff --git a/nim-bindings/src/libchat.nim b/nim-bindings/src/libchat.nim index ed56fc8..ae9748c 100644 --- a/nim-bindings/src/libchat.nim +++ b/nim-bindings/src/libchat.nim @@ -46,7 +46,7 @@ proc createIntroductionBundle*(ctx: LibChat): Result[string, string] = return ok(cast[string](buffer)) ## Create a Private Convo -proc createNewPrivateConvo*(ctx: LibChat, bundle: string, content: seq[byte]): Result[(ConvoHandle, seq[PayloadResult]), string] = +proc createNewPrivateConvo*(ctx: LibChat, bundle: string, content: seq[byte]): Result[(string, seq[PayloadResult]), string] = if ctx.handle == nil: return err("Context handle is nil") @@ -75,7 +75,7 @@ proc createNewPrivateConvo*(ctx: LibChat, bundle: string, content: seq[byte]): R data: p.data.toSeq() ) - let convoId = res.convo_id + let convoId = $res.convo_id # Free the result destroy_convo_result(res) @@ -83,7 +83,7 @@ proc createNewPrivateConvo*(ctx: LibChat, bundle: string, content: seq[byte]): R return ok((convoId, payloads)) ## Send content to an existing conversation -proc sendContent*(ctx: LibChat, convoHandle: ConvoHandle, content: seq[byte]): Result[seq[PayloadResult], string] = +proc sendContent*(ctx: LibChat, convoId: string, content: seq[byte]): Result[seq[PayloadResult], string] = if ctx.handle == nil: return err("Context handle is nil") @@ -92,7 +92,7 @@ proc sendContent*(ctx: LibChat, convoHandle: ConvoHandle, content: seq[byte]): R let res = bindings.send_content( ctx.handle, - convoHandle, + convoId.toSlice(), content.toSlice() )