From 3b90c9d58a8e94cbdf1bd5b1962a5cb5cc72abb8 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 4 Feb 2026 10:18:32 -0800 Subject: [PATCH] Update bindings to include types --- conversations/src/api.rs | 17 +++++++++--- nim-bindings/src/bindings.nim | 50 ++++++++++++++++++++++++----------- nim-bindings/src/libchat.nim | 39 +++++++++++++-------------- 3 files changed, 67 insertions(+), 39 deletions(-) diff --git a/conversations/src/api.rs b/conversations/src/api.rs index d08a436..8d3e9c2 100644 --- a/conversations/src/api.rs +++ b/conversations/src/api.rs @@ -12,7 +12,10 @@ pub enum ErrorCode { UnknownError = -6, } -use crate::context::{Context, Introduction}; +use crate::{ + context::{Context, Introduction}, + types::ContentData, +}; /// Opaque wrapper for Context #[derive_ReprC] @@ -156,7 +159,8 @@ pub fn handle_payload( HandlePayloadResult { error_code: ErrorCode::NotImplemented as i32, convo_id: "".into(), - payloads: safer_ffi::Vec::EMPTY, + content: safer_ffi::Vec::EMPTY, + events: Events::None, } } @@ -188,6 +192,12 @@ pub fn destroy_send_content_result(result: SendContentResult) { drop(result); } +#[derive_ReprC] +#[repr(i32)] +pub enum Events { + None = 0, + NewConvo = 1, +} /// Result structure for create_new_private_convo_safe /// error_code is 0 on success, negative on error (see ErrorCode) #[derive_ReprC] @@ -195,7 +205,8 @@ pub fn destroy_send_content_result(result: SendContentResult) { pub struct HandlePayloadResult { pub error_code: i32, pub convo_id: repr_c::String, - pub payloads: repr_c::Vec, + pub content: repr_c::Vec, + pub events: Events, } /// Free the result from create_new_private_convo_safe diff --git a/nim-bindings/src/bindings.nim b/nim-bindings/src/bindings.nim index c453e0a..e6ee803 100644 --- a/nim-bindings/src/bindings.nim +++ b/nim-bindings/src/bindings.nim @@ -66,12 +66,25 @@ type len*: csize_t cap*: csize_t - ## Result structure for create_intro_bundle + ## Events enum for handle_payload + Events* = enum + evNone = 0 + evNewConvo = 1 + + ## Result structure for send_content ## error_code is 0 on success, negative on error (see ErrorCode) - PayloadResult* = object + SendContentResult* = object error_code*: int32 payloads*: VecPayload + ## Result from handle_payload + ## error_code is 0 on success, negative on error (see ErrorCode) + HandlePayloadResult* = object + error_code*: int32 + convo_id*: ReprCString + content*: VecUint8 + events*: Events + ## Result from create_new_private_convo ## error_code is 0 on success, negative on error (see ErrorCode) NewConvoResult* = object @@ -107,30 +120,30 @@ proc create_new_private_convo*( ): NewConvoResult {.importc, dynlib: CONVERSATIONS_LIB.} ## Sends content to an existing conversation -## Returns: PayloadResult struct - check error_code field (0 = success, negative = error) -## The result must be freed with destroy_payload_result() +## Returns: SendContentResult struct - check error_code field (0 = success, negative = error) +## The result must be freed with destroy_send_content_result() proc send_content*( ctx: ContextHandle, - convo_id: SliceUint8, + convo_id: ReprCString, content: SliceUint8, -): PayloadResult {.importc, dynlib: CONVERSATIONS_LIB.} +): SendContentResult {.importc, dynlib: CONVERSATIONS_LIB.} -## Handles an incoming payload and writes content to caller-provided buffers -## Returns: Number of bytes written to content_out on success (>= 0), negative error code on failure -## conversation_id_out_len is set to the number of bytes written to conversation_id_out +## Handles an incoming payload +## Returns: HandlePayloadResult struct - check error_code field (0 = success, negative = error) +## The result must be freed with destroy_handle_payload_result() proc handle_payload*( ctx: ContextHandle, payload: SliceUint8, - conversation_id_out: SliceUint8, - conversation_id_out_len: ptr uint32, - content_out: SliceUint8, -): int32 {.importc, dynlib: CONVERSATIONS_LIB.} +): HandlePayloadResult {.importc, dynlib: CONVERSATIONS_LIB.} ## Free the result from create_new_private_convo proc destroy_convo_result*(result: NewConvoResult) {.importc, dynlib: CONVERSATIONS_LIB.} -## Free the PayloadResult -proc destroy_payload_result*(result: PayloadResult) {.importc, dynlib: CONVERSATIONS_LIB.} +## Free the SendContentResult +proc destroy_send_content_result*(result: SendContentResult) {.importc, dynlib: CONVERSATIONS_LIB.} + +## Free the HandlePayloadResult +proc destroy_handle_payload_result*(result: HandlePayloadResult) {.importc, dynlib: CONVERSATIONS_LIB.} # ============================================================================ # Helper functions @@ -157,6 +170,13 @@ proc `$`*(s: ReprCString): string = result = newString(s.len) copyMem(addr result[0], s.ptr, s.len) +## Create a ReprCString from a Nim string (borrows memory - string must outlive usage) +proc toReprCString*(s: string): ReprCString = + if s.len == 0: + ReprCString(`ptr`: nil, len: 0, cap: 0) + else: + ReprCString(`ptr`: cast[ptr char](unsafeAddr s[0]), len: csize_t(s.len), cap: csize_t(s.len)) + ## Convert a VecUint8 to a seq[byte] proc toSeq*(v: VecUint8): seq[byte] = if v.ptr == nil or v.len == 0: diff --git a/nim-bindings/src/libchat.nim b/nim-bindings/src/libchat.nim index ae9748c..93431f6 100644 --- a/nim-bindings/src/libchat.nim +++ b/nim-bindings/src/libchat.nim @@ -92,13 +92,13 @@ proc sendContent*(ctx: LibChat, convoId: string, content: seq[byte]): Result[seq let res = bindings.send_content( ctx.handle, - convoId.toSlice(), + convoId.toReprCString(), content.toSlice() ) if res.error_code != 0: result = err("Failed to send content: " & $res.error_code) - destroy_payload_result(res) + destroy_send_content_result(res) return # Convert payloads to Nim types @@ -110,7 +110,7 @@ proc sendContent*(ctx: LibChat, convoId: string, content: seq[byte]): Result[seq data: p.data.toSeq() ) - destroy_payload_result(res) + destroy_send_content_result(res) return ok(payloads) type @@ -126,31 +126,28 @@ proc handlePayload*(ctx: LibChat, payload: seq[byte]): Result[Option[ContentResu if payload.len == 0: return err("payload is zero length") - var conversationIdBuf = newSeq[byte](ctx.buffer_size) - var contentBuf = newSeq[byte](ctx.buffer_size) - var conversationIdLen: uint32 = 0 - - let bytesWritten = bindings.handle_payload( + let res = bindings.handle_payload( ctx.handle, - payload.toSlice(), - conversationIdBuf.toSlice(), - addr conversationIdLen, - contentBuf.toSlice() + payload.toSlice() ) - if bytesWritten < 0: - return err("Failed to handle payload: " & $bytesWritten) + if res.error_code != 0: + result = err("Failed to handle payload: " & $res.error_code) + destroy_handle_payload_result(res) + return - if bytesWritten == 0: + # Check if there's content + if res.content.len == 0: + destroy_handle_payload_result(res) return ok(none(ContentResult)) - conversationIdBuf.setLen(conversationIdLen) - contentBuf.setLen(bytesWritten) + let content = ContentResult( + conversationId: $res.convo_id, + data: res.content.toSeq() + ) - return ok(some(ContentResult( - conversationId: cast[string](conversationIdBuf), - data: contentBuf - ))) + destroy_handle_payload_result(res) + return ok(some(content)) proc `=destroy`(x: var LibChat) =