From fd9d1f5ff42bb1b776e4a34f533f805ea1bed6de Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:48:54 -0800 Subject: [PATCH 1/6] Cleanup --- nim-bindings/src/libchat.nim | 150 +++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 nim-bindings/src/libchat.nim diff --git a/nim-bindings/src/libchat.nim b/nim-bindings/src/libchat.nim new file mode 100644 index 0000000..4b54e73 --- /dev/null +++ b/nim-bindings/src/libchat.nim @@ -0,0 +1,150 @@ +import std/[sequtils, strutils] +import results + +import bindings + +# type +# LibChatResults[T] = Result[T, string] + +type + LibChat* = object + handle: ContextHandle + buffer_size: int + + PayloadResult* = object + address*: string + data*: seq[uint8] + +proc newConversationsContext*(): LibChat = + ## Create a new conversations context + result.handle = create_context() + if result.handle.isNil: + raise newException(IOError, "Failed to create context") + +proc destroy*(ctx: var LibChat) = + ## Destroy the context and free resources + if not ctx.handle.isNil: + destroy_context(ctx.handle) + ctx.handle = nil + +proc getBuffer*(ctx: LibChat): seq[byte] = + newSeq[byte](ctx.buffer_size) + +proc sendContent*(ctx: LibChat, conversationId: string, content: openArray[uint8]): seq[PayloadResult] = + ## Send content and get back payloads to deliver + const + maxPayloads = 10 + payloadMaxLen = 4096 + addrMaxLen = 256 + + # Allocate buffers for payload data + var payloadBuffers: array[maxPayloads, array[payloadMaxLen, uint8]] + var payloadPtrs: array[maxPayloads, ptr uint8] + for i in 0.. 0: unsafeAddr content[0] else: nil + + let numPayloads = generate_payload( + ctx.handle, + conversationId.cstring, + contentPtr, + content.len.csize_t, + maxPayloads.csize_t, + addr addrPtrs[0], + addrMaxLen.csize_t, + addr payloadPtrs[0], + addr maxLen, + addr actualLengths[0] + ) + + if numPayloads < 0: + case numPayloads: + of ErrBadPtr: + raise newException(ValueError, "Bad pointer passed to generate_payload") + of ErrBadConvoId: + raise newException(ValueError, "Bad conversation ID: " & conversationId) + else: + raise newException(IOError, "Unknown error: " & $numPayloads) + + result = @[] + for i in 0.. 0: + copyMem(addr payload.data[0], addr payloadBuffers[i][0], dataLen) + + result.add(payload) + +proc setBufferSize*(ctx: var LibChat, buffer_size: uint32) = + if ctx.handle != nil: + # Update local value + ctx.buffer_size = buffer_size.int + + # Notify libchat what size buffers will be provided + bindings.set_buffer_size(ctx.handle, buffer_size) + +proc createIntroductionBundle*( ctx: LibChat) : Result[string,string] = + if ctx.handle != nil: + + var buffer = ctx.getBuffer() + let buf = cast[ptr UncheckedArray[byte]](addr buffer[0]) + let len = create_intro_bundle(ctx.handle, buf) + + if len < 0: + return err("Failed to create intro bundle") + + buffer.setLen(len) + return ok(cast[string](buffer)) + +proc createNewPrivateConvo*(ctx: LibChat, bundle: string, content: string): Result[(ConvoHandle, seq[byte]), string] = + if ctx.handle == nil: + return err("Context handle is nil") + + # Convert strings to byte pointers + let bundlePtr = if bundle.len > 0: cast[ptr uint8](unsafeAddr bundle[0]) else: return err("bundle is zero length") + let contentPtr = if content.len > 0: cast[ptr uint8](unsafeAddr content[0]) else: return err("content is zero length") + + # Output buffers + var convoHandle: ConvoHandle + var messageOut = ctx.getBuffer() + let messageOutPtr = cast[ptr UncheckedArray[byte]](addr messageOut[0]) + + let result_code = bindings.create_new_private_convo( + ctx.handle, + bundlePtr, + bundle.len.csize_t, + contentPtr, + content.len.csize_t, + cast[ptr uint32](addr convoHandle), + messageOutPtr + ) + + if result_code < 0: + return err("Failed to create private convo: " & $result_code) + + # result_code is bytes written to messageOut + messageOut.setLen(result_code) + + return ok((convoHandle, messageOut)) + + +proc `=destroy`(x: var LibChat) = + # Automatically free handle when the destructor is called + if x.handle != nil: + x.destroy() From d80f608a6f94af5e066e81d992042e8ba611bc0e Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:58:42 -0800 Subject: [PATCH 2/6] remove unneeded code --- nim-bindings/src/libchat.nim | 61 ------------------------------------ 1 file changed, 61 deletions(-) diff --git a/nim-bindings/src/libchat.nim b/nim-bindings/src/libchat.nim index 4b54e73..f1dc5a5 100644 --- a/nim-bindings/src/libchat.nim +++ b/nim-bindings/src/libchat.nim @@ -30,67 +30,6 @@ proc destroy*(ctx: var LibChat) = proc getBuffer*(ctx: LibChat): seq[byte] = newSeq[byte](ctx.buffer_size) -proc sendContent*(ctx: LibChat, conversationId: string, content: openArray[uint8]): seq[PayloadResult] = - ## Send content and get back payloads to deliver - const - maxPayloads = 10 - payloadMaxLen = 4096 - addrMaxLen = 256 - - # Allocate buffers for payload data - var payloadBuffers: array[maxPayloads, array[payloadMaxLen, uint8]] - var payloadPtrs: array[maxPayloads, ptr uint8] - for i in 0.. 0: unsafeAddr content[0] else: nil - - let numPayloads = generate_payload( - ctx.handle, - conversationId.cstring, - contentPtr, - content.len.csize_t, - maxPayloads.csize_t, - addr addrPtrs[0], - addrMaxLen.csize_t, - addr payloadPtrs[0], - addr maxLen, - addr actualLengths[0] - ) - - if numPayloads < 0: - case numPayloads: - of ErrBadPtr: - raise newException(ValueError, "Bad pointer passed to generate_payload") - of ErrBadConvoId: - raise newException(ValueError, "Bad conversation ID: " & conversationId) - else: - raise newException(IOError, "Unknown error: " & $numPayloads) - - result = @[] - for i in 0.. 0: - copyMem(addr payload.data[0], addr payloadBuffers[i][0], dataLen) - - result.add(payload) - proc setBufferSize*(ctx: var LibChat, buffer_size: uint32) = if ctx.handle != nil: # Update local value From d4f0d131f6865f9f9a6e18031aac9d8f2498765f Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:04:39 -0800 Subject: [PATCH 3/6] Update comment style --- nim-bindings/src/bindings.nim | 95 +++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 nim-bindings/src/bindings.nim diff --git a/nim-bindings/src/bindings.nim b/nim-bindings/src/bindings.nim new file mode 100644 index 0000000..8e11271 --- /dev/null +++ b/nim-bindings/src/bindings.nim @@ -0,0 +1,95 @@ +# Nim FFI bindings for libchat conversations library + +import std/[os] + +# Dynamic library path resolution +# Can be overridden at compile time with -d:CONVERSATIONS_LIB:"path/to/lib" +# Or at runtime via LIBCHAT_LIB environment variable +when defined(macosx): + const DEFAULT_LIB_NAME = "liblogos_chat.dylib" +elif defined(linux): + const DEFAULT_LIB_NAME = "liblogos_chat.so" +elif defined(windows): + const DEFAULT_LIB_NAME = "logos_chat.dll" +else: + const DEFAULT_LIB_NAME = "logos_chat" + +# Try to find the library relative to the source file location at compile time +const + thisDir = currentSourcePath().parentDir() + projectRoot = thisDir.parentDir().parentDir() + releaseLibPath = projectRoot / "target" / "release" / DEFAULT_LIB_NAME + debugLibPath = projectRoot / "target" / "debug" / DEFAULT_LIB_NAME + +# Default to release path, can be overridden with -d:CONVERSATIONS_LIB:"..." +const CONVERSATIONS_LIB* {.strdefine.} = releaseLibPath + +# Error codes (must match Rust ErrorCode enum) +const + ErrBadPtr* = -1'i32 + ErrBadConvoId* = -2'i32 + +# Opaque handle type for Context +type ContextHandle* = pointer +type ConvoHandle* = uint32 + +# FFI function imports + +## Creates a new libchat Context +## Returns: Opaque handle to the context. Must be freed with destroy_context() +proc create_context*(): ContextHandle {.importc, dynlib: CONVERSATIONS_LIB.} + +## Destroys a context and frees its memory +## - handle must be a valid pointer from create_context() +## - handle must not be used after this call +proc destroy_context*(handle: ContextHandle) {.importc, dynlib: CONVERSATIONS_LIB.} + + +## Encrypts/encodes content into payloads. +## Returns: Number of payloads created, or negative error code +proc generate_payload*( + handle: ContextHandle, + conversation_id: cstring, + content: ptr uint8, + content_len: csize_t, + max_payload_count: csize_t, + addrs: ptr ptr cchar, + addr_max_len: csize_t, + payload_buffer_ptrs: ptr ptr uint8, + payload_buffer_max_len: ptr csize_t, + output_actual_lengths: ptr csize_t +): int32 {.importc, dynlib: CONVERSATIONS_LIB.} + + +## Decrypts/decodes payloads into content. +## Returns: Number of bytes written to content, or negative error code +proc handle_payload*( + handle: ContextHandle, + payload_data: ptr uint8, + payload_len: csize_t, + content: ptr uint8, + content_max_len: csize_t +): int32 {.importc, dynlib: CONVERSATIONS_LIB.} + +proc set_buffer_size*( + handle: ContextHandle, + buf_size: uint32, +) {.importc, dynlib: CONVERSATIONS_LIB.} + +## Fills provided buffer with a introduction bundle for intializing conversations +proc create_intro_bundle*( + handle: ContextHandle, + bundle_out: ptr UncheckedArray[byte], +): int32 {.importc, dynlib: CONVERSATIONS_LIB.} + +## Fills provided buffer with a introduction bundle for intializing conversations +proc create_new_private_convo*( + handle: ContextHandle, + bundle: ptr uint8, + bundle_size: csize_t, + content: ptr uint8, + content_size: csize_t, + convo_id: ptr uint32, + payload_out: ptr UncheckedArray[byte], +): int32 {.importc, dynlib: CONVERSATIONS_LIB.} + From 0a84498f457bd1cf11a00ed5fdff6d5b1eca6e2b Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:03:39 -0800 Subject: [PATCH 4/6] Update Nim side to support safer_ffi --- nim-bindings/examples/pingpong.nim | 22 +++++ nim-bindings/src/bindings.nim | 154 ++++++++++++++++++++--------- nim-bindings/src/libchat.nim | 89 +++++++++-------- 3 files changed, 173 insertions(+), 92 deletions(-) create mode 100644 nim-bindings/examples/pingpong.nim diff --git a/nim-bindings/examples/pingpong.nim b/nim-bindings/examples/pingpong.nim new file mode 100644 index 0000000..72b43ce --- /dev/null +++ b/nim-bindings/examples/pingpong.nim @@ -0,0 +1,22 @@ +import results + +import ../src/libchat + +proc pingpong() = + + var raya = newConversationsContext() + var saro = newConversationsContext() + + + # Perform out of band Introduction + let intro = raya.createIntroductionBundle().expect("[Raya] Couldn't create intro bundle") + echo "Raya's Intro Bundle: ",intro + + var (convo_sr, payload) = saro.createNewPrivateConvo(intro,"Hey Raya").expect("[Saro] Couldn't create convo") + echo "ConvoHandle:: ", convo_sr + echo "Payload:: ", payload + + + +when isMainModule: + pingpong() diff --git a/nim-bindings/src/bindings.nim b/nim-bindings/src/bindings.nim index 8e11271..0b3fd26 100644 --- a/nim-bindings/src/bindings.nim +++ b/nim-bindings/src/bindings.nim @@ -26,13 +26,60 @@ const CONVERSATIONS_LIB* {.strdefine.} = releaseLibPath # Error codes (must match Rust ErrorCode enum) const + ErrNone* = 0'i32 ErrBadPtr* = -1'i32 ErrBadConvoId* = -2'i32 + ErrBadIntro* = -3'i32 + ErrNotImplemented* = -4'i32 + ErrBufferExceeded* = -5'i32 + ErrUnknownError* = -6'i32 # Opaque handle type for Context type ContextHandle* = pointer type ConvoHandle* = uint32 +type + ## Slice for passing byte arrays to safer_ffi functions + SliceUint8* = object + `ptr`*: ptr uint8 + len*: csize_t + + ## Vector type returned by safer_ffi functions (must be freed) + VecUint8* = object + `ptr`*: ptr uint8 + len*: csize_t + cap*: csize_t + + ## repr_c::String type from safer_ffi + ReprCString* = object + `ptr`*: ptr char + len*: csize_t + cap*: csize_t + + ## Payload structure for FFI (matches Rust Payload struct) + Payload* = object + address*: ReprCString + data*: VecUint8 + + ## Vector of Payloads returned by safer_ffi functions + VecPayload* = object + `ptr`*: ptr Payload + len*: csize_t + cap*: csize_t + + ## Result structure for create_intro_bundle + ## error_code is 0 on success, negative on error (see ErrorCode) + PayloadResult* = object + error_code*: int32 + payloads*: VecPayload + + ## Result from create_new_private_convo + ## error_code is 0 on success, negative on error (see ErrorCode) + NewConvoResult* = object + error_code*: int32 + convo_id*: uint32 + payloads*: VecPayload + # FFI function imports ## Creates a new libchat Context @@ -42,54 +89,67 @@ proc create_context*(): ContextHandle {.importc, dynlib: CONVERSATIONS_LIB.} ## Destroys a context and frees its memory ## - handle must be a valid pointer from create_context() ## - handle must not be used after this call -proc destroy_context*(handle: ContextHandle) {.importc, dynlib: CONVERSATIONS_LIB.} - - -## Encrypts/encodes content into payloads. -## Returns: Number of payloads created, or negative error code -proc generate_payload*( - handle: ContextHandle, - conversation_id: cstring, - content: ptr uint8, - content_len: csize_t, - max_payload_count: csize_t, - addrs: ptr ptr cchar, - addr_max_len: csize_t, - payload_buffer_ptrs: ptr ptr uint8, - payload_buffer_max_len: ptr csize_t, - output_actual_lengths: ptr csize_t -): int32 {.importc, dynlib: CONVERSATIONS_LIB.} - - -## Decrypts/decodes payloads into content. -## Returns: Number of bytes written to content, or negative error code -proc handle_payload*( - handle: ContextHandle, - payload_data: ptr uint8, - payload_len: csize_t, - content: ptr uint8, - content_max_len: csize_t -): int32 {.importc, dynlib: CONVERSATIONS_LIB.} +proc destroy_context*(ctx: ContextHandle) {.importc, dynlib: CONVERSATIONS_LIB.} -proc set_buffer_size*( - handle: ContextHandle, - buf_size: uint32, -) {.importc, dynlib: CONVERSATIONS_LIB.} - -## Fills provided buffer with a introduction bundle for intializing conversations +## Creates an intro bundle for sharing with other users +## Returns: Number of bytes written to bundle_out, or negative error code proc create_intro_bundle*( - handle: ContextHandle, - bundle_out: ptr UncheckedArray[byte], -): int32 {.importc, dynlib: CONVERSATIONS_LIB.} - -## Fills provided buffer with a introduction bundle for intializing conversations -proc create_new_private_convo*( - handle: ContextHandle, - bundle: ptr uint8, - bundle_size: csize_t, - content: ptr uint8, - content_size: csize_t, - convo_id: ptr uint32, - payload_out: ptr UncheckedArray[byte], + ctx: ContextHandle, + bundle_out: SliceUint8, ): int32 {.importc, dynlib: CONVERSATIONS_LIB.} +## Creates a new private conversation +## Returns: NewConvoResult struct - check error_code field (0 = success, negative = error) +## The result must be freed with destroy_convo_result() +proc create_new_private_convo*( + ctx: ContextHandle, + bundle: SliceUint8, + content: SliceUint8, +): NewConvoResult {.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.} + +# ============================================================================ +# Helper functions +# ============================================================================ + +## Create a SliceRefUint8 from a string +proc toSlice*(s: string): SliceUint8 = + if s.len == 0: + SliceUint8(`ptr`: nil, len: 0) + else: + SliceUint8(`ptr`: cast[ptr uint8](unsafeAddr s[0]), len: csize_t(s.len)) + +## Create a SliceRefUint8 from a seq[byte] +proc toSlice*(s: seq[byte]): SliceUint8 = + if s.len == 0: + SliceUint8(`ptr`: nil, len: 0) + else: + SliceUint8(`ptr`: cast[ptr uint8](unsafeAddr s[0]), len: csize_t(s.len)) + +## Convert a ReprCString to a Nim string +proc `$`*(s: ReprCString): string = + if s.ptr == nil or s.len == 0: + return "" + result = newString(s.len) + copyMem(addr result[0], s.ptr, s.len) + +## Convert a VecUint8 to a seq[byte] +proc toSeq*(v: VecUint8): seq[byte] = + if v.ptr == nil or v.len == 0: + return @[] + result = newSeq[byte](v.len) + copyMem(addr result[0], v.ptr, v.len) + +## Access payloads from VecPayload +proc `[]`*(v: VecPayload, i: int): Payload = + assert i >= 0 and csize_t(i) < v.len + cast[ptr UncheckedArray[Payload]](v.ptr)[i] + +## Get length of VecPayload +proc len*(v: VecPayload): int = + int(v.len) diff --git a/nim-bindings/src/libchat.nim b/nim-bindings/src/libchat.nim index f1dc5a5..c0f3f6a 100644 --- a/nim-bindings/src/libchat.nim +++ b/nim-bindings/src/libchat.nim @@ -1,11 +1,6 @@ -import std/[sequtils, strutils] import results - import bindings -# type -# LibChatResults[T] = Result[T, string] - type LibChat* = object handle: ContextHandle @@ -15,72 +10,76 @@ type address*: string data*: seq[uint8] +## Create a new conversations context proc newConversationsContext*(): LibChat = - ## Create a new conversations context + result.handle = create_context() + result.buffer_size = 256 if result.handle.isNil: raise newException(IOError, "Failed to create context") +## Destroy the context and free resources proc destroy*(ctx: var LibChat) = - ## Destroy the context and free resources + if not ctx.handle.isNil: destroy_context(ctx.handle) ctx.handle = nil +## Helper proc to create buffer of sufficient size proc getBuffer*(ctx: LibChat): seq[byte] = newSeq[byte](ctx.buffer_size) -proc setBufferSize*(ctx: var LibChat, buffer_size: uint32) = - if ctx.handle != nil: - # Update local value - ctx.buffer_size = buffer_size.int - - # Notify libchat what size buffers will be provided - bindings.set_buffer_size(ctx.handle, buffer_size) - -proc createIntroductionBundle*( ctx: LibChat) : Result[string,string] = - if ctx.handle != nil: +## Generate a Introduction Bundle +proc createIntroductionBundle*(ctx: LibChat): Result[string, string] = + if ctx.handle == nil: + return err("Context handle is nil") - var buffer = ctx.getBuffer() - let buf = cast[ptr UncheckedArray[byte]](addr buffer[0]) - let len = create_intro_bundle(ctx.handle, buf) + var buffer = ctx.getBuffer() + var slice = buffer.toSlice() + let len = create_intro_bundle(ctx.handle, slice) - if len < 0: - return err("Failed to create intro bundle") + if len < 0: + return err("Failed to create intro bundle: " & $len) - buffer.setLen(len) - return ok(cast[string](buffer)) + buffer.setLen(len) + return ok(cast[string](buffer)) -proc createNewPrivateConvo*(ctx: LibChat, bundle: string, content: string): Result[(ConvoHandle, seq[byte]), string] = +## Create a Private Convo +proc createNewPrivateConvo*(ctx: LibChat, bundle: string, content: string): Result[(ConvoHandle, seq[PayloadResult]), string] = if ctx.handle == nil: return err("Context handle is nil") - # Convert strings to byte pointers - let bundlePtr = if bundle.len > 0: cast[ptr uint8](unsafeAddr bundle[0]) else: return err("bundle is zero length") - let contentPtr = if content.len > 0: cast[ptr uint8](unsafeAddr content[0]) else: return err("content is zero length") - - # Output buffers - var convoHandle: ConvoHandle - var messageOut = ctx.getBuffer() - let messageOutPtr = cast[ptr UncheckedArray[byte]](addr messageOut[0]) + if bundle.len == 0: + return err("bundle is zero length") + if content.len == 0: + return err("content is zero length") - let result_code = bindings.create_new_private_convo( + let res = bindings.create_new_private_convo( ctx.handle, - bundlePtr, - bundle.len.csize_t, - contentPtr, - content.len.csize_t, - cast[ptr uint32](addr convoHandle), - messageOutPtr + bundle.toSlice(), + content.toSlice() ) - if result_code < 0: - return err("Failed to create private convo: " & $result_code) + if res.error_code != 0: + result = err("Failed to create private convo: " & $res.error_code) + destroy_convo_result(res) + return + + # Convert payloads to Nim types + var payloads = newSeq[PayloadResult](res.payloads.len) + for i in 0 ..< res.payloads.len: + let p = res.payloads[int(i)] + payloads[int(i)] = PayloadResult( + address: $p.address, + data: p.data.toSeq() + ) + + let convoId = res.convo_id - # result_code is bytes written to messageOut - messageOut.setLen(result_code) + # Free the result + destroy_convo_result(res) - return ok((convoHandle, messageOut)) + return ok((convoId, payloads)) proc `=destroy`(x: var LibChat) = From 872acec3a20fdff7e288d78b51475ac34c2418f4 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:29:28 -0800 Subject: [PATCH 5/6] Fix Introduction from impl --- conversations/src/api.rs | 3 +-- conversations/src/inbox/introduction.rs | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/conversations/src/api.rs b/conversations/src/api.rs index 399c45d..9fd7c09 100644 --- a/conversations/src/api.rs +++ b/conversations/src/api.rs @@ -71,8 +71,7 @@ pub fn create_new_private_convo( content: c_slice::Ref<'_, u8>, ) -> NewConvoResult { // Convert input bundle to Introduction - let s = String::from_utf8_lossy(&bundle).to_string(); - let Ok(intro) = Introduction::try_from(s) else { + let Ok(intro) = Introduction::try_from(bundle.as_slice()) else { return NewConvoResult { error_code: ErrorCode::BadIntro as i32, convo_id: 0, diff --git a/conversations/src/inbox/introduction.rs b/conversations/src/inbox/introduction.rs index 2966293..0955e11 100644 --- a/conversations/src/inbox/introduction.rs +++ b/conversations/src/inbox/introduction.rs @@ -31,12 +31,11 @@ impl Into> for Introduction { } } -impl TryFrom> for Introduction { +impl TryFrom<&[u8]> for Introduction { type Error = ChatError; - fn try_from(value: Vec) -> Result { - let str_value = - String::from_utf8(value).map_err(|_| ChatError::BadParsing("Introduction"))?; + fn try_from(value: &[u8]) -> Result { + let str_value = String::from_utf8_lossy(value); let parts: Vec<&str> = str_value.splitn(3, ':').collect(); if parts[0] != "Bundle" { From 9f4fbb930081a88397a2f10362b636de52760b2b Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:31:51 -0800 Subject: [PATCH 6/6] Updates --- .gitignore | 3 +++ conversations/Cargo.toml | 2 +- nim-bindings/README.md | 8 ++++++++ nim-bindings/conversations_example.nimble | 21 +++++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 nim-bindings/README.md create mode 100644 nim-bindings/conversations_example.nimble diff --git a/.gitignore b/.gitignore index 1fca2de..d395ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ target # Compiled binary **/ffi_nim_example +/nim-bindings/examples/pingpong +/nim-bindings/libchat # Temporary data folder tmp + diff --git a/conversations/Cargo.toml b/conversations/Cargo.toml index 2809a58..0fd90ed 100644 --- a/conversations/Cargo.toml +++ b/conversations/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [lib] -crate-type = ["staticlib"] +crate-type = ["staticlib","dylib"] [dependencies] blake2.workspace = true diff --git a/nim-bindings/README.md b/nim-bindings/README.md new file mode 100644 index 0000000..b97671b --- /dev/null +++ b/nim-bindings/README.md @@ -0,0 +1,8 @@ +# Nim-bindings + +A Nim wrapping class that exposes LibChat functionality. + + +## Getting Started + +`nimble pingpong` - Run the pingpong example. \ No newline at end of file diff --git a/nim-bindings/conversations_example.nimble b/nim-bindings/conversations_example.nimble new file mode 100644 index 0000000..3670d1a --- /dev/null +++ b/nim-bindings/conversations_example.nimble @@ -0,0 +1,21 @@ +# Package + +version = "0.1.0" +author = "libchat" +description = "Nim Bindings for LibChat" +license = "MIT" +srcDir = "src" +bin = @["libchat"] + + +# Dependencies + +requires "nim >= 2.2.4" +requires "results" + +# Build Rust library before compiling Nim +before build: + exec "cargo build --release --manifest-path ../Cargo.toml" + +task pingpong, "Run pingpong example": + exec "nim c -r --path:src examples/pingpong.nim" \ No newline at end of file