feat: #37 Batch1 — detect_material_type/read_image_meta/read_text_stats/parse_text out-pointer FFI

新增 4 个 _separate 函数绕过 RustBuffer struct-passing:
- ffi_zx_document_ffi_detect_material_type_separate
- ffi_zx_document_ffi_read_image_meta_separate
- ffi_zx_document_ffi_read_text_stats_separate
- ffi_zx_document_ffi_parse_text_separate

提取 write_result_to_out! 宏和 read_str_input helper 减少样板代码。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-06 12:45:56 +08:00
parent 65c9ab8a50
commit c09caab0e6

View File

@ -242,6 +242,88 @@ fn clear_exported_events(count: u32) {
zx_document_core::events::clear_exported_events(count as usize)
}
/// Helper: serialize a Result<T, DocumentError> 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 <Result<_, DocumentError> as LowerReturn<UniFfiTag>>::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<String> {
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<T, E> 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 {