libchat/crates/client-ffi/client_ffi.h

208 lines
7.2 KiB
C
Raw Normal View History

2026-03-26 22:38:38 +01:00
#ifndef CLIENT_FFI_H
#define CLIENT_FFI_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------
* Opaque handle returned by client_create.
* ------------------------------------------------------------------ */
typedef struct ClientHandle ClientHandle_t;
/* ------------------------------------------------------------------
* C delivery callback.
*
* Called for each outbound envelope. `addr_ptr/addr_len` is the
* delivery address (UTF-8, not NUL-terminated). `data_ptr/data_len`
* is the encrypted payload. `userdata` is the pointer supplied to
* client_create. Return 0 on success, negative on error.
* ------------------------------------------------------------------ */
typedef int32_t (*DeliverFn_t)(
const uint8_t *addr_ptr, size_t addr_len,
const uint8_t *data_ptr, size_t data_len,
void *userdata);
/* ------------------------------------------------------------------
* Error codes (match crates/client-ffi/src/api.rs ErrorCode)
* ------------------------------------------------------------------ */
#define CLIENT_FFI_OK 0
#define CLIENT_FFI_ERR_BAD_UTF8 (-1)
#define CLIENT_FFI_ERR_BAD_INTRO (-2)
#define CLIENT_FFI_ERR_DELIVERY (-3)
#define CLIENT_FFI_ERR_UNKNOWN (-4)
/* ------------------------------------------------------------------
* Result structs all heap-allocated fields are owned by the caller
* and must be freed via the matching destroy_* function.
* ------------------------------------------------------------------ */
typedef struct {
int32_t error_code;
uint8_t *intro_bytes_ptr;
size_t intro_bytes_len;
} CreateIntroResult_t;
typedef struct {
int32_t error_code;
/* UTF-8, NOT NUL-terminated */
uint8_t *convo_id_ptr;
size_t convo_id_len;
} CreateConvoResult_t;
typedef struct {
int32_t error_code;
bool has_content;
bool is_new_convo;
/* UTF-8, NOT NUL-terminated. NULL when has_content is false. */
uint8_t *convo_id_ptr;
size_t convo_id_len;
/* NULL when has_content is false. */
uint8_t *content_ptr;
size_t content_len;
} PushInboundResult_t;
/* ------------------------------------------------------------------
* Lifecycle
* ------------------------------------------------------------------ */
/**
* Create an ephemeral in-memory client.
*
* @param name_ptr UTF-8 installation name (need not be NUL-terminated).
* @param name_len Byte length of name_ptr.
* @param callback Non-null delivery callback.
* @param userdata Opaque pointer forwarded to each callback invocation.
* @return Opaque handle, or NULL on error. Free with client_destroy.
*/
ClientHandle_t *client_create(
const uint8_t *name_ptr, size_t name_len,
DeliverFn_t callback,
void *userdata);
/**
* Destroy a client handle. Must not be used after this call.
*/
void client_destroy(ClientHandle_t *handle);
/* ------------------------------------------------------------------
* Identity
* ------------------------------------------------------------------ */
/**
* Return a pointer to the installation name bytes (UTF-8, not NUL-terminated).
* The pointer is valid until the next mutating call or client_destroy.
* The caller must NOT free it.
*
* @param out_len Receives the byte length of the returned string.
*/
const uint8_t *client_installation_name(const ClientHandle_t *handle, size_t *out_len);
/* ------------------------------------------------------------------
* Intro bundle
* ------------------------------------------------------------------ */
/**
* Generate a serialised introduction bundle for out-of-band sharing.
* Always call destroy_create_intro_result when done.
*/
void client_create_intro_bundle(ClientHandle_t *handle, CreateIntroResult_t *out);
/** Free the result from client_create_intro_bundle. */
void destroy_create_intro_result(CreateIntroResult_t *result);
/* ------------------------------------------------------------------
* Create conversation
* ------------------------------------------------------------------ */
/**
* Parse an intro bundle and initiate a private conversation.
* Outbound envelopes are dispatched through the delivery callback.
* Always call destroy_create_convo_result when done.
*/
void client_create_conversation(
ClientHandle_t *handle,
const uint8_t *bundle_ptr, size_t bundle_len,
const uint8_t *content_ptr, size_t content_len,
CreateConvoResult_t *out);
/** Free the result from client_create_conversation. */
void destroy_create_convo_result(CreateConvoResult_t *result);
/* ------------------------------------------------------------------
* Send message
* ------------------------------------------------------------------ */
/**
* Encrypt content and dispatch outbound envelopes.
*
* @param convo_id_ptr UTF-8 conversation ID (not NUL-terminated).
* @return CLIENT_FFI_OK on success, error code otherwise.
*/
int32_t client_send_message(
ClientHandle_t *handle,
const uint8_t *convo_id_ptr, size_t convo_id_len,
const uint8_t *content_ptr, size_t content_len);
/* ------------------------------------------------------------------
* Push inbound
* ------------------------------------------------------------------ */
/**
* Decrypt an inbound payload. has_content is false for protocol frames.
* Always call destroy_push_inbound_result when error_code == CLIENT_FFI_OK.
*/
void client_push_inbound(
ClientHandle_t *handle,
const uint8_t *payload_ptr, size_t payload_len,
PushInboundResult_t *out);
/** Free the result from client_push_inbound. */
void destroy_push_inbound_result(PushInboundResult_t *result);
/* ------------------------------------------------------------------
* ClientOps vtable used by c-test so C can call the Rust client-ffi
* functions indirectly via function pointers supplied by the Rust harness.
*
* This avoids the linker-ordering problem that arises when C archives
* are searched before Rust rlibs: the C code has no direct undefined
* references to Rust symbols; it only uses the pointers it receives.
* ------------------------------------------------------------------ */
typedef struct {
ClientHandle_t *(*create)(
const uint8_t *name_ptr, size_t name_len,
DeliverFn_t callback, void *userdata);
void (*destroy)(ClientHandle_t *handle);
void (*create_intro_bundle)(ClientHandle_t *handle, CreateIntroResult_t *out);
void (*destroy_intro_result)(CreateIntroResult_t *result);
void (*create_conversation)(
ClientHandle_t *handle,
const uint8_t *bundle_ptr, size_t bundle_len,
const uint8_t *content_ptr, size_t content_len,
CreateConvoResult_t *out);
void (*destroy_convo_result)(CreateConvoResult_t *result);
int32_t (*send_message)(
ClientHandle_t *handle,
const uint8_t *convo_id_ptr, size_t convo_id_len,
const uint8_t *content_ptr, size_t content_len);
void (*push_inbound)(
ClientHandle_t *handle,
const uint8_t *payload_ptr, size_t payload_len,
PushInboundResult_t *out);
void (*destroy_inbound_result)(PushInboundResult_t *result);
} ClientOps;
#ifdef __cplusplus
}
#endif
#endif /* CLIENT_FFI_H */