Use safer_ffi for all functions

This commit is contained in:
Jazz Turner-Baggs 2026-02-06 13:09:44 -08:00
parent 135347cdd0
commit 06fe6bd2d3
No known key found for this signature in database

View File

@ -1,6 +1,16 @@
use safer_ffi::prelude::*; use safer_ffi::prelude::*;
// Must only contain negative values, values cannot be changed once set. // Must only contain negative values, values cannot be changed once set.
use safer_ffi::{
String, derive_ReprC, ffi_export,
prelude::{c_slice, repr_c},
};
use crate::{
context::{Context, Introduction},
errors::ChatError,
types::ContentData,
};
#[repr(i32)] #[repr(i32)]
pub enum ErrorCode { pub enum ErrorCode {
None = 0, None = 0,
@ -13,6 +23,9 @@ pub enum ErrorCode {
} }
use crate::context::{Context, Introduction}; use crate::context::{Context, Introduction};
pub fn is_ok(error: i32) -> bool {
error == ErrorCode::None as i32
}
/// Opaque wrapper for Context /// Opaque wrapper for Context
#[derive_ReprC] #[derive_ReprC]
@ -45,18 +58,17 @@ pub fn destroy_context(ctx: repr_c::Box<ContextHandle>) {
/// Returns the number of bytes written to bundle_out /// 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).
#[ffi_export] #[ffi_export]
pub fn create_intro_bundle(ctx: &mut ContextHandle, mut bundle_out: c_slice::Mut<'_, u8>) -> i32 { pub fn create_intro_bundle(ctx: &mut ContextHandle) -> CreateIntroResult {
let Ok(bundle) = ctx.0.create_intro_bundle() else { match ctx.0.create_intro_bundle() {
return ErrorCode::UnknownError as i32; Ok(v) => CreateIntroResult {
}; error_code: ErrorCode::None as i32,
intro_bytes: v.into(),
// Check buffer is large enough },
if bundle_out.len() < bundle.len() { Err(_e) => CreateIntroResult {
return ErrorCode::BufferExceeded as i32; error_code: ErrorCode::UnknownError as i32,
intro_bytes: repr_c::Vec::EMPTY,
},
} }
bundle_out[..bundle.len()].copy_from_slice(&bundle);
bundle.len() as i32
} }
/// Creates a new private conversation /// Creates a new private conversation
@ -108,11 +120,11 @@ 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>,
) -> PayloadResult { ) -> 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 PayloadResult { return SendContentResult {
error_code: ErrorCode::UnknownError as i32, error_code: ErrorCode::UnknownError as i32,
payloads: safer_ffi::Vec::EMPTY, payloads: safer_ffi::Vec::EMPTY,
}; };
@ -127,7 +139,7 @@ pub fn send_content(
}) })
.collect(); .collect();
PayloadResult { SendContentResult {
error_code: 0, error_code: 0,
payloads: ffi_payloads.into(), payloads: ffi_payloads.into(),
} }
@ -143,35 +155,31 @@ pub fn send_content(
pub fn handle_payload( pub fn handle_payload(
ctx: &mut ContextHandle, ctx: &mut ContextHandle,
payload: c_slice::Ref<'_, u8>, payload: c_slice::Ref<'_, u8>,
mut conversation_id_out: c_slice::Mut<'_, u8>, ) -> HandlePayloadResult {
conversation_id_out_len: Out<'_, u32>,
mut content_out: c_slice::Mut<'_, u8>,
) -> i32 {
match ctx.0.handle_payload(&payload) { match ctx.0.handle_payload(&payload) {
Ok(Some(content)) => { Ok(o) => o.into(),
let convo_id_bytes = content.conversation_id.as_bytes(); Err(e) => e.into(),
if conversation_id_out.len() < convo_id_bytes.len() {
return ErrorCode::BufferExceeded as i32;
}
if content_out.len() < content.data.len() {
return ErrorCode::BufferExceeded as i32;
}
conversation_id_out[..convo_id_bytes.len()].copy_from_slice(convo_id_bytes);
conversation_id_out_len.write(convo_id_bytes.len() as u32);
content_out[..content.data.len()].copy_from_slice(&content.data);
content.data.len() as i32
}
_ => 0,
} }
} }
/// Result structure for create_intro_bundle
/// error_code is 0 on success, negative on error (see ErrorCode)
#[derive_ReprC]
#[repr(C)]
pub struct CreateIntroResult {
pub error_code: i32,
pub intro_bytes: repr_c::Vec<u8>,
}
// ============================================================================ // ============================================================================
// safer_ffi implementation // safer_ffi implementation
// =============================================================================================================================== // ===============================================================================================================================
/// Free the result from create_intro_bundle
#[ffi_export]
pub fn destroy_intro_result(result: CreateIntroResult) {
drop(result);
}
/// Payload structure for FFI /// Payload structure for FFI
#[derive(Debug)] #[derive(Debug)]
@ -186,18 +194,71 @@ pub struct Payload {
/// error_code is 0 on success, negative on error (see ErrorCode) /// error_code is 0 on success, negative on error (see ErrorCode)
#[derive_ReprC] #[derive_ReprC]
#[repr(C)] #[repr(C)]
pub struct PayloadResult { pub struct SendContentResult {
pub error_code: i32, pub error_code: i32,
pub payloads: repr_c::Vec<Payload>, pub payloads: repr_c::Vec<Payload>,
} }
/// Free the result from create_intro_bundle_safe /// Free the result from create_intro_bundle_safe
/// Free the result from send_content
#[ffi_export] #[ffi_export]
pub fn destroy_payload_result(result: PayloadResult) { pub fn destroy_send_content_result(result: SendContentResult) {
drop(result);
}
/// Result structure for handle_payload
/// error_code is 0 on success, negative on error (see ErrorCode)
#[derive(Debug)]
#[derive_ReprC]
#[repr(C)]
pub struct HandlePayloadResult {
pub error_code: i32,
pub convo_id: repr_c::String,
pub content: repr_c::Vec<u8>,
}
/// Free the result from handle_payload
#[ffi_export]
pub fn destroy_handle_payload_result(result: HandlePayloadResult) {
drop(result); drop(result);
} }
/// Result structure for create_new_private_convo_safe /// Result structure for create_new_private_convo_safe
impl From<ContentData> for HandlePayloadResult {
fn from(value: ContentData) -> Self {
HandlePayloadResult {
error_code: ErrorCode::None as i32,
convo_id: value.conversation_id.into(),
content: value.data.into(),
}
}
}
impl From<Option<ContentData>> for HandlePayloadResult {
fn from(value: Option<ContentData>) -> Self {
if let Some(content) = value {
content.into()
} else {
HandlePayloadResult {
error_code: ErrorCode::None as i32,
convo_id: repr_c::String::EMPTY,
content: repr_c::Vec::EMPTY,
}
}
}
}
impl From<ChatError> for HandlePayloadResult {
fn from(_value: ChatError) -> Self {
HandlePayloadResult {
// TODO: (P2) Translate ChatError into ErrorCode
error_code: ErrorCode::UnknownError as i32,
convo_id: String::EMPTY,
content: repr_c::Vec::EMPTY,
}
}
}
/// error_code is 0 on success, negative on error (see ErrorCode) /// error_code is 0 on success, negative on error (see ErrorCode)
#[derive_ReprC] #[derive_ReprC]
#[repr(C)] #[repr(C)]