diff --git a/crates/zx_document_ffi/src/lib.rs b/crates/zx_document_ffi/src/lib.rs index 3eea041..d9a3b88 100644 --- a/crates/zx_document_ffi/src/lib.rs +++ b/crates/zx_document_ffi/src/lib.rs @@ -324,6 +324,98 @@ pub extern "C" fn ffi_zx_document_ffi_parse_text_separate( write_result_to_out!(result, out_capacity, out_len, out_data, out_error_code); } +// ─── Helper: lift from raw buffer fields ─── + +macro_rules! lift_from_raw { + ($T:ty, $capacity:expr, $len:expr, $data:expr) => {{ + let v = unsafe { Vec::from_raw_parts($data as *mut u8, $len as usize, $capacity as usize) }; + let buf = uniffi::RustBuffer::from_vec(v); + <$T as uniffi::Lift>::try_lift(buf).expect(concat!("failed to lift ", stringify!($T))) + }}; +} + +// ─── Batch 2: complex type input/output ─── + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_push_reading_event_separate( + event_cap: u64, event_len: u64, event_data: *const u8, +) { + let event: ReadingEvent = lift_from_raw!(ReadingEvent, event_cap, event_len, event_data); + crate::push_reading_event(event); +} + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_update_reading_position_separate( + mid_len: i32, mid_data: *const u8, + pos_cap: u64, pos_len: u64, pos_data: *const u8, + out_error_code: *mut i8, +) { + let material_id = match unsafe { read_str_input(mid_len, mid_data, out_error_code) } { + Some(s) => s, None => return, + }; + unsafe { *out_error_code = 0; } + let position: ReadingPosition = lift_from_raw!(ReadingPosition, pos_cap, pos_len, pos_data); + crate::update_reading_position(material_id, position); +} + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_export_pending_events_separate( + out_capacity: *mut u64, out_len: *mut u64, out_data: *mut *mut u8, out_error_code: *mut i8, +) { + let result: Vec = crate::export_pending_events(); + write_result_to_out!(Ok::<_, DocumentError>(result), out_capacity, out_len, out_data, out_error_code); +} + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_create_note_anchor_separate( + mid_len: i32, mid_data: *const u8, + pos_cap: u64, pos_len: u64, pos_data: *const u8, pos_has_value: i8, + out_capacity: *mut u64, out_len: *mut u64, out_data: *mut *mut u8, out_error_code: *mut i8, +) { + let material_id = match unsafe { read_str_input(mid_len, mid_data, out_error_code) } { + Some(s) => s, None => return, + }; + let position: Option = if pos_has_value != 0 { + Some(lift_from_raw!(ReadingPosition, pos_cap, pos_len, pos_data)) + } else { + None + }; + let result: Result = Ok(crate::create_note_anchor(material_id, position)); + write_result_to_out!(result, out_capacity, out_len, out_data, out_error_code); +} + +// ─── Batch 3: search functions ─── + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_search_markdown_blocks_separate( + blocks_cap: u64, blocks_len: u64, blocks_data: *const u8, + query_len: i32, query_data: *const u8, + out_capacity: *mut u64, out_len: *mut u64, out_data: *mut *mut u8, out_error_code: *mut i8, +) { + let query = match unsafe { read_str_input(query_len, query_data, out_error_code) } { + Some(s) => s, None => return, + }; + let blocks: Vec = lift_from_raw!(Vec, blocks_cap, blocks_len, blocks_data); + let result: Vec = crate::search_markdown_blocks(blocks, query); + write_result_to_out!(Ok::<_, DocumentError>(result), out_capacity, out_len, out_data, out_error_code); +} + +#[no_mangle] +pub extern "C" fn ffi_zx_document_ffi_search_text_content_separate( + content_len: i32, content_data: *const u8, + query_len: i32, query_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(content_len, content_data, out_error_code) } { + Some(s) => s, None => return, + }; + let query = match unsafe { read_str_input(query_len, query_data, out_error_code) } { + Some(s) => s, None => return, + }; + let result: Vec = crate::search_text_content(content, query); + write_result_to_out!(Ok::<_, DocumentError>(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 {