diff --git a/crates/zx_document_ffi/src/lib.rs b/crates/zx_document_ffi/src/lib.rs index 7a94d4e..3eea041 100644 --- a/crates/zx_document_ffi/src/lib.rs +++ b/crates/zx_document_ffi/src/lib.rs @@ -242,6 +242,88 @@ fn clear_exported_events(count: u32) { zx_document_core::events::clear_exported_events(count as usize) } +/// Helper: serialize a Result into out-pointers. +/// Generic over T; the RustBuffer methods are accessed via the concrete type +/// after the `lower_return` call. +macro_rules! write_result_to_out { + ($result:expr, $out_capacity:ident, $out_len:ident, $out_data:ident, $out_error_code:ident) => {{ + use uniffi::LowerReturn; + match as LowerReturn>::lower_return($result) { + Ok(buf) => { + unsafe { + *$out_capacity = buf.capacity() as u64; + *$out_len = buf.len() as u64; + *$out_data = buf.data_pointer() as *mut u8; + *$out_error_code = 0; + } + std::mem::forget(buf); + } + Err(_) => { + unsafe { *$out_error_code = -1; } + } + } + }}; +} + +/// Helper: read a UTF-8 string from raw bytes, or set error and return false. +unsafe fn read_str_input(len: i32, data: *const u8, out_error_code: *mut i8) -> Option { + let slice = std::slice::from_raw_parts(data, len as usize); + match std::str::from_utf8(slice) { + Ok(s) => Some(s.to_string()), + Err(_) => { *out_error_code = -1; None }, + } +} + +// ─── Batch 1 out-pointer functions: String input → Result output ─── + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_detect_material_type_separate( + len: i32, data: *const u8, + out_capacity: *mut u64, out_len: *mut u64, out_data: *mut *mut u8, out_error_code: *mut i8, +) { + let file_path = match unsafe { read_str_input(len, data, out_error_code) } { + Some(s) => s, None => return, + }; + let result = crate::detect_material_type(file_path); + write_result_to_out!(result, out_capacity, out_len, out_data, out_error_code); +} + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_read_image_meta_separate( + len: i32, data: *const u8, + out_capacity: *mut u64, out_len: *mut u64, out_data: *mut *mut u8, out_error_code: *mut i8, +) { + let file_path = match unsafe { read_str_input(len, data, out_error_code) } { + Some(s) => s, None => return, + }; + let result = crate::read_image_meta(file_path); + write_result_to_out!(result, out_capacity, out_len, out_data, out_error_code); +} + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_read_text_stats_separate( + len: i32, data: *const u8, + out_capacity: *mut u64, out_len: *mut u64, out_data: *mut *mut u8, out_error_code: *mut i8, +) { + let file_path = match unsafe { read_str_input(len, data, out_error_code) } { + Some(s) => s, None => return, + }; + let result = crate::read_text_stats(file_path); + write_result_to_out!(result, out_capacity, out_len, out_data, out_error_code); +} + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_parse_text_separate( + len: i32, data: *const u8, + out_capacity: *mut u64, out_len: *mut u64, out_data: *mut *mut u8, out_error_code: *mut i8, +) { + let content = match unsafe { read_str_input(len, data, out_error_code) } { + Some(s) => s, None => return, + }; + let result = crate::parse_text(content); + write_result_to_out!(result, out_capacity, out_len, out_data, out_error_code); +} + // Reverse conversion: FFI DocumentBlock → core DocumentBlock, used by search. fn core_block_from_ffi(block: DocumentBlock) -> core_blocks::DocumentBlock { match block {