mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-03-27 14:43:06 +00:00
Merge branch 'main' into fix/sigsegv
This commit is contained in:
commit
a283d91e89
7
.cargo/config.toml
Normal file
7
.cargo/config.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[env]
|
||||||
|
# Pin the macOS deployment target so vendored C/C++ builds (SQLCipher, OpenSSL)
|
||||||
|
# compile with the same minimum version as the Nim linker expects.
|
||||||
|
# Without this, the host SDK version is used (currently 15.5), causing
|
||||||
|
# "was built for newer macOS version" linker warnings.
|
||||||
|
# This is caused by nimble and cargo defaulting to different targets.
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = { value = "15.0", force = false }
|
||||||
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
@ -2,11 +2,11 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -36,3 +36,21 @@ jobs:
|
|||||||
- run: rustup update stable && rustup default stable
|
- run: rustup update stable && rustup default stable
|
||||||
- run: rustup component add rustfmt
|
- run: rustup component add rustfmt
|
||||||
- run: cargo fmt --all -- --check
|
- run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
nim-bindings-test:
|
||||||
|
name: Nim Bindings Test
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: rustup update stable && rustup default stable
|
||||||
|
- name: Install Nim via choosenim
|
||||||
|
run: |
|
||||||
|
curl https://nim-lang.org/choosenim/init.sh -sSf | sh -s -- -y
|
||||||
|
echo "$HOME/.nimble/bin" >> $GITHUB_PATH
|
||||||
|
- run: nimble install -dy
|
||||||
|
working-directory: nim-bindings
|
||||||
|
- run: nimble pingpong
|
||||||
|
working-directory: nim-bindings
|
||||||
|
|||||||
@ -55,9 +55,12 @@ pub fn create_context(name: repr_c::String) -> repr_c::Box<ContextHandle> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the friendly name of the contexts installation.
|
/// Returns the friendly name of the contexts installation.
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// The result is written through `out` (Nim's calling convention for large struct returns).
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn installation_name(ctx: &ContextHandle) -> repr_c::String {
|
pub fn installation_name(ctx: &ContextHandle, out: &mut repr_c::String) {
|
||||||
ctx.0.installation_name().to_string().into()
|
*out = ctx.0.installation_name().to_string().into();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroys a conversation store and frees its memory
|
/// Destroys a conversation store and frees its memory
|
||||||
@ -74,11 +77,13 @@ pub fn destroy_context(ctx: repr_c::Box<ContextHandle>) {
|
|||||||
/// Creates an intro bundle for sharing with other users
|
/// Creates an intro bundle for sharing with other users
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// Returns the number of bytes written to bundle_out
|
|
||||||
/// Check error_code field: 0 means success, negative values indicate errors (see ErrorCode).
|
/// Check error_code field: 0 means success, negative values indicate errors (see ErrorCode).
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// The result is written through `out` (Nim's calling convention for large struct returns).
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn create_intro_bundle(ctx: &mut ContextHandle) -> CreateIntroResult {
|
pub fn create_intro_bundle(ctx: &mut ContextHandle, out: &mut CreateIntroResult) {
|
||||||
match ctx.0.create_intro_bundle() {
|
*out = match ctx.0.create_intro_bundle() {
|
||||||
Ok(v) => CreateIntroResult {
|
Ok(v) => CreateIntroResult {
|
||||||
error_code: ErrorCode::None as i32,
|
error_code: ErrorCode::None as i32,
|
||||||
intro_bytes: v.into(),
|
intro_bytes: v.into(),
|
||||||
@ -87,7 +92,7 @@ pub fn create_intro_bundle(ctx: &mut ContextHandle) -> CreateIntroResult {
|
|||||||
error_code: ErrorCode::UnknownError as i32,
|
error_code: ErrorCode::UnknownError as i32,
|
||||||
intro_bytes: repr_c::Vec::EMPTY,
|
intro_bytes: repr_c::Vec::EMPTY,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new private conversation
|
/// Creates a new private conversation
|
||||||
@ -95,19 +100,24 @@ pub fn create_intro_bundle(ctx: &mut ContextHandle) -> CreateIntroResult {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// Returns a struct with payloads that must be sent, the conversation_id that was created.
|
/// Returns a struct with payloads that must be sent, the conversation_id that was created.
|
||||||
/// The NewConvoResult must be freed.
|
/// The NewConvoResult must be freed.
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// The result is written through `out` (Nim's calling convention for large struct returns).
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn create_new_private_convo(
|
pub fn create_new_private_convo(
|
||||||
ctx: &mut ContextHandle,
|
ctx: &mut ContextHandle,
|
||||||
bundle: c_slice::Ref<'_, u8>,
|
bundle: c_slice::Ref<'_, u8>,
|
||||||
content: c_slice::Ref<'_, u8>,
|
content: c_slice::Ref<'_, u8>,
|
||||||
) -> NewConvoResult {
|
out: &mut NewConvoResult,
|
||||||
|
) {
|
||||||
// Convert input bundle to Introduction
|
// Convert input bundle to Introduction
|
||||||
let Ok(intro) = Introduction::try_from(bundle.as_slice()) else {
|
let Ok(intro) = Introduction::try_from(bundle.as_slice()) else {
|
||||||
return NewConvoResult {
|
*out = NewConvoResult {
|
||||||
error_code: ErrorCode::BadIntro as i32,
|
error_code: ErrorCode::BadIntro as i32,
|
||||||
convo_id: "".into(),
|
convo_id: "".into(),
|
||||||
payloads: Vec::new().into(),
|
payloads: Vec::new().into(),
|
||||||
};
|
};
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create conversation
|
// Create conversation
|
||||||
@ -122,11 +132,11 @@ pub fn create_new_private_convo(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
NewConvoResult {
|
*out = NewConvoResult {
|
||||||
error_code: 0,
|
error_code: 0,
|
||||||
convo_id: convo_id.to_string().into(),
|
convo_id: convo_id.to_string().into(),
|
||||||
payloads: ffi_payloads.into(),
|
payloads: ffi_payloads.into(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends content to an existing conversation
|
/// Sends content to an existing conversation
|
||||||
@ -134,19 +144,24 @@ pub fn create_new_private_convo(
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// Returns a PayloadResult with payloads that must be delivered to participants.
|
/// Returns a PayloadResult with payloads that must be delivered to participants.
|
||||||
/// Check error_code field: 0 means success, negative values indicate errors (see ErrorCode).
|
/// Check error_code field: 0 means success, negative values indicate errors (see ErrorCode).
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// The result is written through `out` (Nim's calling convention for large struct returns).
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn send_content(
|
pub fn send_content(
|
||||||
ctx: &mut ContextHandle,
|
ctx: &mut ContextHandle,
|
||||||
convo_id: repr_c::String,
|
convo_id: repr_c::String,
|
||||||
content: c_slice::Ref<'_, u8>,
|
content: c_slice::Ref<'_, u8>,
|
||||||
) -> SendContentResult {
|
out: &mut SendContentResult,
|
||||||
|
) {
|
||||||
let payloads = match ctx.0.send_content(&convo_id, &content) {
|
let payloads = match ctx.0.send_content(&convo_id, &content) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return SendContentResult {
|
*out = SendContentResult {
|
||||||
error_code: ErrorCode::UnknownError as i32,
|
error_code: ErrorCode::UnknownError as i32,
|
||||||
payloads: safer_ffi::Vec::EMPTY,
|
payloads: safer_ffi::Vec::EMPTY,
|
||||||
};
|
};
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -158,10 +173,10 @@ pub fn send_content(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
SendContentResult {
|
*out = SendContentResult {
|
||||||
error_code: 0,
|
error_code: 0,
|
||||||
payloads: ffi_payloads.into(),
|
payloads: ffi_payloads.into(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles an incoming payload
|
/// Handles an incoming payload
|
||||||
@ -170,15 +185,19 @@ pub fn send_content(
|
|||||||
/// Returns HandlePayloadResult
|
/// Returns HandlePayloadResult
|
||||||
/// This call does not always generate content. If data is zero bytes long then there
|
/// This call does not always generate content. If data is zero bytes long then there
|
||||||
/// is no data, and the converation_id should be ignored.
|
/// is no data, and the converation_id should be ignored.
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// The result is written through `out` (Nim's calling convention for large struct returns).
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn handle_payload(
|
pub fn handle_payload(
|
||||||
ctx: &mut ContextHandle,
|
ctx: &mut ContextHandle,
|
||||||
payload: c_slice::Ref<'_, u8>,
|
payload: c_slice::Ref<'_, u8>,
|
||||||
) -> HandlePayloadResult {
|
out: &mut HandlePayloadResult,
|
||||||
match ctx.0.handle_payload(&payload) {
|
) {
|
||||||
|
*out = match ctx.0.handle_payload(&payload) {
|
||||||
Ok(o) => o.into(),
|
Ok(o) => o.into(),
|
||||||
Err(e) => e.into(),
|
Err(e) => e.into(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
@ -195,9 +214,13 @@ pub struct CreateIntroResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Free the result from create_intro_bundle
|
/// Free the result from create_intro_bundle
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// Takes `&mut` instead of ownership because Nim always passes large structs as a pointer;
|
||||||
|
/// accepting the struct by value would be an ABI mismatch on the caller side.
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn destroy_intro_result(result: CreateIntroResult) {
|
pub fn destroy_intro_result(result: &mut CreateIntroResult) {
|
||||||
drop(result);
|
unsafe { std::ptr::drop_in_place(result) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Payload structure for FFI
|
/// Payload structure for FFI
|
||||||
@ -219,9 +242,13 @@ pub struct SendContentResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Free the result from send_content
|
/// Free the result from send_content
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// Takes `&mut` instead of ownership because Nim always passes large structs as a pointer;
|
||||||
|
/// accepting the struct by value would be an ABI mismatch on the caller side.
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn destroy_send_content_result(result: SendContentResult) {
|
pub fn destroy_send_content_result(result: &mut SendContentResult) {
|
||||||
drop(result);
|
unsafe { std::ptr::drop_in_place(result) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result structure for handle_payload
|
/// Result structure for handle_payload
|
||||||
@ -237,9 +264,13 @@ pub struct HandlePayloadResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Free the result from handle_payload
|
/// Free the result from handle_payload
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// Takes `&mut` instead of ownership because Nim always passes large structs as a pointer;
|
||||||
|
/// accepting the struct by value would be an ABI mismatch on the caller side.
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn destroy_handle_payload_result(result: HandlePayloadResult) {
|
pub fn destroy_handle_payload_result(result: &mut HandlePayloadResult) {
|
||||||
drop(result);
|
unsafe { std::ptr::drop_in_place(result) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ContentData> for HandlePayloadResult {
|
impl From<ContentData> for HandlePayloadResult {
|
||||||
@ -291,7 +322,11 @@ pub struct NewConvoResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Free the result from create_new_private_convo
|
/// Free the result from create_new_private_convo
|
||||||
|
///
|
||||||
|
/// # ABI note
|
||||||
|
/// Takes `&mut` instead of ownership because Nim always passes large structs as a pointer;
|
||||||
|
/// accepting the struct by value would be an ABI mismatch on the caller side.
|
||||||
#[ffi_export]
|
#[ffi_export]
|
||||||
pub fn destroy_convo_result(result: NewConvoResult) {
|
pub fn destroy_convo_result(result: &mut NewConvoResult) {
|
||||||
drop(result);
|
unsafe { std::ptr::drop_in_place(result) }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,11 @@ mod tests {
|
|||||||
let mut raya = create_context("raya".into());
|
let mut raya = create_context("raya".into());
|
||||||
|
|
||||||
// Raya Creates Bundle and Sends to Saro
|
// Raya Creates Bundle and Sends to Saro
|
||||||
let intro_result = create_intro_bundle(&mut raya);
|
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));
|
assert!(is_ok(intro_result.error_code));
|
||||||
|
|
||||||
let raya_bundle = intro_result.intro_bytes.as_ref();
|
let raya_bundle = intro_result.intro_bytes.as_ref();
|
||||||
@ -33,13 +37,24 @@ mod tests {
|
|||||||
// Saro creates a new conversation with Raya
|
// Saro creates a new conversation with Raya
|
||||||
let content: &[u8] = "hello".as_bytes();
|
let content: &[u8] = "hello".as_bytes();
|
||||||
|
|
||||||
let convo_result = create_new_private_convo(&mut saro, raya_bundle, content.into());
|
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));
|
assert!(is_ok(convo_result.error_code));
|
||||||
|
|
||||||
// Raya recieves initial message
|
// Raya recieves initial message
|
||||||
let payload = convo_result.payloads.first().unwrap();
|
let payload = convo_result.payloads.first().unwrap();
|
||||||
|
|
||||||
let handle_result = handle_payload(&mut raya, payload.data.as_ref());
|
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));
|
assert!(is_ok(handle_result.error_code));
|
||||||
|
|
||||||
// Check that the Content sent was the content received
|
// Check that the Content sent was the content received
|
||||||
|
|||||||
@ -13,9 +13,14 @@ bin = @["libchat"]
|
|||||||
requires "nim >= 2.2.4"
|
requires "nim >= 2.2.4"
|
||||||
requires "results"
|
requires "results"
|
||||||
|
|
||||||
# Build Rust library before compiling Nim
|
proc buildRust() =
|
||||||
before build:
|
|
||||||
exec "cargo build --release --manifest-path ../Cargo.toml"
|
exec "cargo build --release --manifest-path ../Cargo.toml"
|
||||||
|
|
||||||
|
|
||||||
|
# Build Rust library before compiling Nim
|
||||||
|
before build:
|
||||||
|
buildRust()
|
||||||
|
|
||||||
task pingpong, "Run pingpong example":
|
task pingpong, "Run pingpong example":
|
||||||
exec "nim c -r --path:src examples/pingpong.nim"
|
buildRust()
|
||||||
|
exec "nim c -r --path:src --passL:../target/release/liblibchat.a --passL:-lm examples/pingpong.nim"
|
||||||
|
|||||||
@ -1,29 +1,5 @@
|
|||||||
# Nim FFI bindings for libchat conversations library
|
# 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 = "liblibchat.dylib"
|
|
||||||
elif defined(linux):
|
|
||||||
const DEFAULT_LIB_NAME = "liblibchat.so"
|
|
||||||
elif defined(windows):
|
|
||||||
const DEFAULT_LIB_NAME = "libchat.dll"
|
|
||||||
else:
|
|
||||||
const DEFAULT_LIB_NAME = "libchat"
|
|
||||||
|
|
||||||
# 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)
|
# Error codes (must match Rust ErrorCode enum)
|
||||||
const
|
const
|
||||||
ErrNone* = 0'i32
|
ErrNone* = 0'i32
|
||||||
@ -97,23 +73,23 @@ type
|
|||||||
|
|
||||||
## Creates a new libchat Context
|
## Creates a new libchat Context
|
||||||
## Returns: Opaque handle to the context. Must be freed with destroy_context()
|
## Returns: Opaque handle to the context. Must be freed with destroy_context()
|
||||||
proc create_context*(name: ReprCString): ContextHandle {.importc, dynlib: CONVERSATIONS_LIB.}
|
proc create_context*(name: ReprCString): ContextHandle {.importc.}
|
||||||
|
|
||||||
## Returns the friendly name of the context's identity
|
## Returns the friendly name of the context's identity
|
||||||
## The result must be freed by the caller (repr_c::String ownership transfers)
|
## The result must be freed by the caller (repr_c::String ownership transfers)
|
||||||
proc installation_name*(ctx: ContextHandle): ReprCString {.importc, dynlib: CONVERSATIONS_LIB.}
|
proc installation_name*(ctx: ContextHandle): ReprCString {.importc.}
|
||||||
|
|
||||||
## Destroys a context and frees its memory
|
## Destroys a context and frees its memory
|
||||||
## - handle must be a valid pointer from create_context()
|
## - handle must be a valid pointer from create_context()
|
||||||
## - handle must not be used after this call
|
## - handle must not be used after this call
|
||||||
proc destroy_context*(ctx: ContextHandle) {.importc, dynlib: CONVERSATIONS_LIB.}
|
proc destroy_context*(ctx: ContextHandle) {.importc.}
|
||||||
|
|
||||||
## Creates an intro bundle for sharing with other users
|
## Creates an intro bundle for sharing with other users
|
||||||
## Returns: CreateIntroResult struct - check error_code field (0 = success, negative = error)
|
## Returns: CreateIntroResult struct - check error_code field (0 = success, negative = error)
|
||||||
## The result must be freed with destroy_intro_result()
|
## The result must be freed with destroy_intro_result()
|
||||||
proc create_intro_bundle*(
|
proc create_intro_bundle*(
|
||||||
ctx: ContextHandle,
|
ctx: ContextHandle,
|
||||||
): CreateIntroResult {.importc, dynlib: CONVERSATIONS_LIB.}
|
): CreateIntroResult {.importc.}
|
||||||
|
|
||||||
## Creates a new private conversation
|
## Creates a new private conversation
|
||||||
## Returns: NewConvoResult struct - check error_code field (0 = success, negative = error)
|
## Returns: NewConvoResult struct - check error_code field (0 = success, negative = error)
|
||||||
@ -122,7 +98,7 @@ proc create_new_private_convo*(
|
|||||||
ctx: ContextHandle,
|
ctx: ContextHandle,
|
||||||
bundle: SliceUint8,
|
bundle: SliceUint8,
|
||||||
content: SliceUint8,
|
content: SliceUint8,
|
||||||
): NewConvoResult {.importc, dynlib: CONVERSATIONS_LIB.}
|
): NewConvoResult {.importc.}
|
||||||
|
|
||||||
## Sends content to an existing conversation
|
## Sends content to an existing conversation
|
||||||
## Returns: SendContentResult struct - check error_code field (0 = success, negative = error)
|
## Returns: SendContentResult struct - check error_code field (0 = success, negative = error)
|
||||||
@ -131,7 +107,7 @@ proc send_content*(
|
|||||||
ctx: ContextHandle,
|
ctx: ContextHandle,
|
||||||
convo_id: ReprCString,
|
convo_id: ReprCString,
|
||||||
content: SliceUint8,
|
content: SliceUint8,
|
||||||
): SendContentResult {.importc, dynlib: CONVERSATIONS_LIB.}
|
): SendContentResult {.importc.}
|
||||||
|
|
||||||
## Handles an incoming payload
|
## Handles an incoming payload
|
||||||
## Returns: HandlePayloadResult struct - check error_code field (0 = success, negative = error)
|
## Returns: HandlePayloadResult struct - check error_code field (0 = success, negative = error)
|
||||||
@ -141,19 +117,19 @@ proc send_content*(
|
|||||||
proc handle_payload*(
|
proc handle_payload*(
|
||||||
ctx: ContextHandle,
|
ctx: ContextHandle,
|
||||||
payload: SliceUint8,
|
payload: SliceUint8,
|
||||||
): HandlePayloadResult {.importc, dynlib: CONVERSATIONS_LIB.}
|
): HandlePayloadResult {.importc.}
|
||||||
|
|
||||||
## Free the result from create_intro_bundle
|
## Free the result from create_intro_bundle
|
||||||
proc destroy_intro_result*(result: CreateIntroResult) {.importc, dynlib: CONVERSATIONS_LIB.}
|
proc destroy_intro_result*(result: CreateIntroResult) {.importc.}
|
||||||
|
|
||||||
## Free the result from create_new_private_convo
|
## Free the result from create_new_private_convo
|
||||||
proc destroy_convo_result*(result: NewConvoResult) {.importc, dynlib: CONVERSATIONS_LIB.}
|
proc destroy_convo_result*(result: NewConvoResult) {.importc.}
|
||||||
|
|
||||||
## Free the result from send_content
|
## Free the result from send_content
|
||||||
proc destroy_send_content_result*(result: SendContentResult) {.importc, dynlib: CONVERSATIONS_LIB.}
|
proc destroy_send_content_result*(result: SendContentResult) {.importc.}
|
||||||
|
|
||||||
## Free the result from handle_payload
|
## Free the result from handle_payload
|
||||||
proc destroy_handle_payload_result*(result: HandlePayloadResult) {.importc, dynlib: CONVERSATIONS_LIB.}
|
proc destroy_handle_payload_result*(result: HandlePayloadResult) {.importc.}
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Helper functions
|
# Helper functions
|
||||||
|
|||||||
@ -44,11 +44,10 @@ proc createIntroductionBundle*(ctx: LibChat): Result[seq[byte], string] =
|
|||||||
return err("Context handle is nil")
|
return err("Context handle is nil")
|
||||||
|
|
||||||
let res = create_intro_bundle(ctx.handle)
|
let res = create_intro_bundle(ctx.handle)
|
||||||
|
defer: destroy_intro_result(res)
|
||||||
|
|
||||||
if res.error_code != ErrNone:
|
if res.error_code != ErrNone:
|
||||||
result = err("Failed to create private convo: " & $res.error_code)
|
return err("Failed to create intro bundle: " & $res.error_code)
|
||||||
destroy_intro_result(res)
|
|
||||||
return
|
|
||||||
|
|
||||||
return ok(res.intro_bytes.toSeq())
|
return ok(res.intro_bytes.toSeq())
|
||||||
|
|
||||||
@ -62,32 +61,18 @@ proc createNewPrivateConvo*(ctx: LibChat, bundle: seq[byte], content: seq[byte])
|
|||||||
if content.len == 0:
|
if content.len == 0:
|
||||||
return err("content is zero length")
|
return err("content is zero length")
|
||||||
|
|
||||||
let res = bindings.create_new_private_convo(
|
let res = bindings.create_new_private_convo(ctx.handle, bundle.toSlice(), content.toSlice())
|
||||||
ctx.handle,
|
defer: destroy_convo_result(res)
|
||||||
bundle.toSlice(),
|
|
||||||
content.toSlice()
|
|
||||||
)
|
|
||||||
|
|
||||||
if res.error_code != 0:
|
if res.error_code != 0:
|
||||||
result = err("Failed to create private convo: " & $res.error_code)
|
return 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)
|
var payloads = newSeq[PayloadResult](res.payloads.len)
|
||||||
for i in 0 ..< res.payloads.len:
|
for i in 0 ..< res.payloads.len:
|
||||||
let p = res.payloads[int(i)]
|
let p = res.payloads[int(i)]
|
||||||
payloads[int(i)] = PayloadResult(
|
payloads[int(i)] = PayloadResult(address: $p.address, data: p.data.toSeq())
|
||||||
address: $p.address,
|
|
||||||
data: p.data.toSeq()
|
|
||||||
)
|
|
||||||
|
|
||||||
let convoId = $res.convo_id
|
return ok(($res.convo_id, payloads))
|
||||||
|
|
||||||
# Free the result
|
|
||||||
destroy_convo_result(res)
|
|
||||||
|
|
||||||
return ok((convoId, payloads))
|
|
||||||
|
|
||||||
## Send content to an existing conversation
|
## Send content to an existing conversation
|
||||||
proc sendContent*(ctx: LibChat, convoId: string, content: seq[byte]): Result[seq[PayloadResult], string] =
|
proc sendContent*(ctx: LibChat, convoId: string, content: seq[byte]): Result[seq[PayloadResult], string] =
|
||||||
@ -97,24 +82,13 @@ proc sendContent*(ctx: LibChat, convoId: string, content: seq[byte]): Result[seq
|
|||||||
if content.len == 0:
|
if content.len == 0:
|
||||||
return err("content is zero length")
|
return err("content is zero length")
|
||||||
|
|
||||||
let res = bindings.send_content(
|
let res = bindings.send_content(ctx.handle, convoId.toReprCString, content.toSlice())
|
||||||
ctx.handle,
|
defer: destroy_send_content_result(res)
|
||||||
convoId.toReprCString,
|
|
||||||
content.toSlice()
|
|
||||||
)
|
|
||||||
|
|
||||||
if res.error_code != 0:
|
if res.error_code != 0:
|
||||||
result = err("Failed to send content: " & $res.error_code)
|
return err("Failed to send content: " & $res.error_code)
|
||||||
destroy_send_content_result(res)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
let payloads = res.payloads.toSeq().mapIt(PayloadResult(address: $it.address, data: it.data.toSeq()))
|
||||||
let payloads = res.payloads.toSeq().mapIt(PayloadResult(
|
|
||||||
address: $it.address,
|
|
||||||
data: it.data.toSeq()
|
|
||||||
))
|
|
||||||
|
|
||||||
destroy_send_content_result(res)
|
|
||||||
return ok(payloads)
|
return ok(payloads)
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -131,14 +105,8 @@ proc handlePayload*(ctx: LibChat, payload: seq[byte]): Result[Option[ContentResu
|
|||||||
if payload.len == 0:
|
if payload.len == 0:
|
||||||
return err("payload is zero length")
|
return err("payload is zero length")
|
||||||
|
|
||||||
var conversationIdBuf = newSeq[byte](ctx.buffer_size)
|
let res = bindings.handle_payload(ctx.handle, payload.toSlice())
|
||||||
var contentBuf = newSeq[byte](ctx.buffer_size)
|
defer: destroy_handle_payload_result(res)
|
||||||
var conversationIdLen: uint32 = 0
|
|
||||||
|
|
||||||
let res = bindings.handle_payload(
|
|
||||||
ctx.handle,
|
|
||||||
payload.toSlice(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if res.error_code != ErrNone:
|
if res.error_code != ErrNone:
|
||||||
return err("Failed to handle payload: " & $res.error_code)
|
return err("Failed to handle payload: " & $res.error_code)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user