fix: add reload_stale_events_v2 FFI export + update UDL with V2 types + XCFramework rebuild
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
3b0625ffbb
commit
a81a9d7e1f
Binary file not shown.
Binary file not shown.
910
bindings/ios/device/Headers/zx_documentFFI.h
Normal file
910
bindings/ios/device/Headers/zx_documentFFI.h
Normal file
@ -0,0 +1,910 @@
|
||||
// This file was autogenerated by some hot garbage in the `uniffi` crate.
|
||||
// Trust me, you don't want to mess with it!
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// The following structs are used to implement the lowest level
|
||||
// of the FFI, and thus useful to multiple uniffied crates.
|
||||
// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
|
||||
#ifdef UNIFFI_SHARED_H
|
||||
// We also try to prevent mixing versions of shared uniffi header structs.
|
||||
// If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4
|
||||
#ifndef UNIFFI_SHARED_HEADER_V4
|
||||
#error Combining helper code from multiple versions of uniffi is not supported
|
||||
#endif // ndef UNIFFI_SHARED_HEADER_V4
|
||||
#else
|
||||
#define UNIFFI_SHARED_H
|
||||
#define UNIFFI_SHARED_HEADER_V4
|
||||
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
|
||||
|
||||
typedef struct RustBuffer
|
||||
{
|
||||
uint64_t capacity;
|
||||
uint64_t len;
|
||||
uint8_t *_Nullable data;
|
||||
} RustBuffer;
|
||||
|
||||
typedef struct ForeignBytes
|
||||
{
|
||||
int32_t len;
|
||||
const uint8_t *_Nullable data;
|
||||
} ForeignBytes;
|
||||
|
||||
// Error definitions
|
||||
typedef struct RustCallStatus {
|
||||
int8_t code;
|
||||
RustBuffer errorBuf;
|
||||
} RustCallStatus;
|
||||
|
||||
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
|
||||
#endif // def UNIFFI_SHARED_H
|
||||
#ifndef UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK
|
||||
#define UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK
|
||||
typedef void (*UniffiRustFutureContinuationCallback)(uint64_t, int8_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK
|
||||
typedef void (*UniffiForeignFutureDroppedCallback)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE
|
||||
#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE
|
||||
typedef void (*UniffiCallbackInterfaceFree)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_CLONE
|
||||
#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_CLONE
|
||||
typedef uint64_t (*UniffiCallbackInterfaceClone)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK_STRUCT
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK_STRUCT
|
||||
typedef struct UniffiForeignFutureDroppedCallbackStruct {
|
||||
uint64_t handle;
|
||||
UniffiForeignFutureDroppedCallback _Nonnull free;
|
||||
} UniffiForeignFutureDroppedCallbackStruct;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U8
|
||||
typedef struct UniffiForeignFutureResultU8 {
|
||||
uint8_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU8;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8
|
||||
typedef void (*UniffiForeignFutureCompleteU8)(uint64_t, UniffiForeignFutureResultU8
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I8
|
||||
typedef struct UniffiForeignFutureResultI8 {
|
||||
int8_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI8;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8
|
||||
typedef void (*UniffiForeignFutureCompleteI8)(uint64_t, UniffiForeignFutureResultI8
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U16
|
||||
typedef struct UniffiForeignFutureResultU16 {
|
||||
uint16_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU16;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16
|
||||
typedef void (*UniffiForeignFutureCompleteU16)(uint64_t, UniffiForeignFutureResultU16
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I16
|
||||
typedef struct UniffiForeignFutureResultI16 {
|
||||
int16_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI16;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16
|
||||
typedef void (*UniffiForeignFutureCompleteI16)(uint64_t, UniffiForeignFutureResultI16
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U32
|
||||
typedef struct UniffiForeignFutureResultU32 {
|
||||
uint32_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32
|
||||
typedef void (*UniffiForeignFutureCompleteU32)(uint64_t, UniffiForeignFutureResultU32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I32
|
||||
typedef struct UniffiForeignFutureResultI32 {
|
||||
int32_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32
|
||||
typedef void (*UniffiForeignFutureCompleteI32)(uint64_t, UniffiForeignFutureResultI32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U64
|
||||
typedef struct UniffiForeignFutureResultU64 {
|
||||
uint64_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64
|
||||
typedef void (*UniffiForeignFutureCompleteU64)(uint64_t, UniffiForeignFutureResultU64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I64
|
||||
typedef struct UniffiForeignFutureResultI64 {
|
||||
int64_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64
|
||||
typedef void (*UniffiForeignFutureCompleteI64)(uint64_t, UniffiForeignFutureResultI64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F32
|
||||
typedef struct UniffiForeignFutureResultF32 {
|
||||
float returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultF32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32
|
||||
typedef void (*UniffiForeignFutureCompleteF32)(uint64_t, UniffiForeignFutureResultF32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F64
|
||||
typedef struct UniffiForeignFutureResultF64 {
|
||||
double returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultF64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64
|
||||
typedef void (*UniffiForeignFutureCompleteF64)(uint64_t, UniffiForeignFutureResultF64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_RUST_BUFFER
|
||||
typedef struct UniffiForeignFutureResultRustBuffer {
|
||||
RustBuffer returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultRustBuffer;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER
|
||||
typedef void (*UniffiForeignFutureCompleteRustBuffer)(uint64_t, UniffiForeignFutureResultRustBuffer
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_VOID
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_VOID
|
||||
typedef struct UniffiForeignFutureResultVoid {
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultVoid;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID
|
||||
typedef void (*UniffiForeignFutureCompleteVoid)(uint64_t, UniffiForeignFutureResultVoid
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_ACK_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_ACK_EVENTS_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_ack_events_v2(RustBuffer event_ids, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_cleanup_stale_sessions_ffi(int64_t now_ms, int64_t max_age_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
void uniffi_zx_document_ffi_fn_func_clear_exported_events(uint32_t count, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLOSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLOSE_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_close_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor_from_search(RustBuffer material_id, RustBuffer result, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_detect_material_type(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events_v2(uint32_t limit, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_extract_pdf_text_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_get_office_preview_config_ffi(RustBuffer material_type, uint64_t file_size, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_IS_OFFICE_TYPE_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_IS_OFFICE_TYPE_FFI
|
||||
int8_t uniffi_zx_document_ffi_fn_func_is_office_type_ffi(RustBuffer material_type, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_MARK_EVENTS_FAILED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_MARK_EVENTS_FAILED_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_mark_events_failed_v2(RustBuffer event_ids, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_parse_markdown(RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_TEXT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_TEXT
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_parse_text(RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PAUSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PAUSE_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_pause_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_HEARTBEAT_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_HEARTBEAT_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_heartbeat_v2(RustBuffer session_id, RustBuffer material_id, uint32_t active_seconds_delta, RustBuffer position, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_marked_as_read_v2(RustBuffer session_id, RustBuffer material_id, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_material_closed_v2(RustBuffer session_id, RustBuffer material_id, uint32_t active_seconds_delta, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_material_opened_v2(RustBuffer session_id, RustBuffer material_id, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_position_changed_v2(RustBuffer session_id, RustBuffer material_id, RustBuffer position, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||
void uniffi_zx_document_ffi_fn_func_push_reading_event(RustBuffer event, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_epub_chapters_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_METADATA_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_epub_metadata_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_image_meta(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_PDF_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_PDF_METADATA_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_pdf_metadata_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_text_stats(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_reload_stale_events_v2(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_restore_position_from_anchor(RustBuffer anchor, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESUME_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESUME_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_resume_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_epub_chapters_ffi(RustBuffer chapter_ids, RustBuffer chapter_texts, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_markdown_blocks(RustBuffer blocks, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_PDF_PAGES
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_PDF_PAGES
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_pdf_pages(RustBuffer page_numbers, RustBuffer page_texts, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_text_content(RustBuffer content, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_START_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_START_READING_SESSION_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_start_reading_session_v2(RustBuffer material, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||
void uniffi_zx_document_ffi_fn_func_update_reading_position(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_ALLOC
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_ALLOC
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_alloc(uint64_t size, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FROM_BYTES
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FROM_BYTES
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FREE
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FREE
|
||||
void ffi_zx_document_ffi_rustbuffer_free(RustBuffer buf, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_RESERVE
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_RESERVE
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_reserve(RustBuffer buf, uint64_t additional, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U8
|
||||
void ffi_zx_document_ffi_rust_future_poll_u8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U8
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U8
|
||||
void ffi_zx_document_ffi_rust_future_free_u8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U8
|
||||
uint8_t ffi_zx_document_ffi_rust_future_complete_u8(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I8
|
||||
void ffi_zx_document_ffi_rust_future_poll_i8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I8
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I8
|
||||
void ffi_zx_document_ffi_rust_future_free_i8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I8
|
||||
int8_t ffi_zx_document_ffi_rust_future_complete_i8(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U16
|
||||
void ffi_zx_document_ffi_rust_future_poll_u16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U16
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U16
|
||||
void ffi_zx_document_ffi_rust_future_free_u16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U16
|
||||
uint16_t ffi_zx_document_ffi_rust_future_complete_u16(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I16
|
||||
void ffi_zx_document_ffi_rust_future_poll_i16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I16
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I16
|
||||
void ffi_zx_document_ffi_rust_future_free_i16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I16
|
||||
int16_t ffi_zx_document_ffi_rust_future_complete_i16(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U32
|
||||
void ffi_zx_document_ffi_rust_future_poll_u32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U32
|
||||
void ffi_zx_document_ffi_rust_future_free_u32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U32
|
||||
uint32_t ffi_zx_document_ffi_rust_future_complete_u32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I32
|
||||
void ffi_zx_document_ffi_rust_future_poll_i32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I32
|
||||
void ffi_zx_document_ffi_rust_future_free_i32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I32
|
||||
int32_t ffi_zx_document_ffi_rust_future_complete_i32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U64
|
||||
void ffi_zx_document_ffi_rust_future_poll_u64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U64
|
||||
void ffi_zx_document_ffi_rust_future_free_u64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U64
|
||||
uint64_t ffi_zx_document_ffi_rust_future_complete_u64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I64
|
||||
void ffi_zx_document_ffi_rust_future_poll_i64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I64
|
||||
void ffi_zx_document_ffi_rust_future_free_i64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I64
|
||||
int64_t ffi_zx_document_ffi_rust_future_complete_i64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F32
|
||||
void ffi_zx_document_ffi_rust_future_poll_f32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_f32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F32
|
||||
void ffi_zx_document_ffi_rust_future_free_f32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F32
|
||||
float ffi_zx_document_ffi_rust_future_complete_f32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F64
|
||||
void ffi_zx_document_ffi_rust_future_poll_f64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_f64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F64
|
||||
void ffi_zx_document_ffi_rust_future_free_f64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F64
|
||||
double ffi_zx_document_ffi_rust_future_complete_f64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_poll_rust_buffer(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_cancel_rust_buffer(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_free_rust_buffer(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_RUST_BUFFER
|
||||
RustBuffer ffi_zx_document_ffi_rust_future_complete_rust_buffer(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_VOID
|
||||
void ffi_zx_document_ffi_rust_future_poll_void(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_VOID
|
||||
void ffi_zx_document_ffi_rust_future_cancel_void(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_VOID
|
||||
void ffi_zx_document_ffi_rust_future_free_void(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||
void ffi_zx_document_ffi_rust_future_complete_void(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_ACK_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_ACK_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_ack_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_cleanup_stale_sessions_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_clear_exported_events(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLOSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLOSE_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_close_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor_from_search(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_detect_material_type(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_extract_pdf_text_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_get_office_preview_config_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_IS_OFFICE_TYPE_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_IS_OFFICE_TYPE_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_is_office_type_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_MARK_EVENTS_FAILED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_MARK_EVENTS_FAILED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_mark_events_failed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_parse_markdown(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_parse_text(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PAUSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PAUSE_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_pause_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_HEARTBEAT_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_HEARTBEAT_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_heartbeat_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_marked_as_read_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_material_closed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_material_opened_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_position_changed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_reading_event(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_epub_chapters_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_METADATA_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_epub_metadata_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_image_meta(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_PDF_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_PDF_METADATA_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_pdf_metadata_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_text_stats(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_reload_stale_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_restore_position_from_anchor(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESUME_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESUME_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_resume_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_epub_chapters_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_markdown_blocks(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_PDF_PAGES
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_PDF_PAGES
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_pdf_pages(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_text_content(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_START_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_START_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_start_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_update_reading_position(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_UNIFFI_CONTRACT_VERSION
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_UNIFFI_CONTRACT_VERSION
|
||||
uint32_t ffi_zx_document_ffi_uniffi_contract_version(void
|
||||
|
||||
);
|
||||
#endif
|
||||
|
||||
4
bindings/ios/device/Modules/module.modulemap
Normal file
4
bindings/ios/device/Modules/module.modulemap
Normal file
@ -0,0 +1,4 @@
|
||||
framework module zx_documentFFI {
|
||||
header "zx_documentFFI.h"
|
||||
export *
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -242,17 +242,37 @@ typedef struct UniffiForeignFutureResultVoid {
|
||||
typedef void (*UniffiForeignFutureCompleteVoid)(uint64_t, UniffiForeignFutureResultVoid
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_ACK_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_ACK_EVENTS_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_ack_events_v2(RustBuffer event_ids, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_cleanup_stale_sessions_ffi(int64_t now_ms, int64_t max_age_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
void uniffi_zx_document_ffi_fn_func_clear_exported_events(uint32_t count, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLOSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLOSE_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_close_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor_from_search(RustBuffer material_id, RustBuffer result, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_detect_material_type(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
@ -262,6 +282,31 @@ RustBuffer uniffi_zx_document_ffi_fn_func_detect_material_type(RustBuffer file_p
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events_v2(uint32_t limit, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_extract_pdf_text_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_get_office_preview_config_ffi(RustBuffer material_type, uint64_t file_size, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_IS_OFFICE_TYPE_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_IS_OFFICE_TYPE_FFI
|
||||
int8_t uniffi_zx_document_ffi_fn_func_is_office_type_ffi(RustBuffer material_type, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_MARK_EVENTS_FAILED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_MARK_EVENTS_FAILED_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_mark_events_failed_v2(RustBuffer event_ids, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN
|
||||
@ -274,31 +319,107 @@ RustBuffer uniffi_zx_document_ffi_fn_func_parse_markdown(RustBuffer content, Rus
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_parse_text(RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PAUSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PAUSE_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_pause_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_HEARTBEAT_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_HEARTBEAT_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_heartbeat_v2(RustBuffer session_id, RustBuffer material_id, uint32_t active_seconds_delta, RustBuffer position, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_marked_as_read_v2(RustBuffer session_id, RustBuffer material_id, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_material_closed_v2(RustBuffer session_id, RustBuffer material_id, uint32_t active_seconds_delta, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_material_opened_v2(RustBuffer session_id, RustBuffer material_id, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_position_changed_v2(RustBuffer session_id, RustBuffer material_id, RustBuffer position, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||
void uniffi_zx_document_ffi_fn_func_push_reading_event(RustBuffer event, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_epub_chapters_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_METADATA_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_epub_metadata_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_image_meta(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_PDF_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_PDF_METADATA_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_pdf_metadata_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_text_stats(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_reload_stale_events_v2(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_restore_position_from_anchor(RustBuffer anchor, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESUME_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESUME_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_resume_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_epub_chapters_ffi(RustBuffer chapter_ids, RustBuffer chapter_texts, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_markdown_blocks(RustBuffer blocks, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_PDF_PAGES
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_PDF_PAGES
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_pdf_pages(RustBuffer page_numbers, RustBuffer page_texts, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_text_content(RustBuffer content, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_START_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_START_READING_SESSION_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_start_reading_session_v2(RustBuffer material, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||
void uniffi_zx_document_ffi_fn_func_update_reading_position(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||
@ -562,18 +683,42 @@ void ffi_zx_document_ffi_rust_future_free_void(uint64_t handle
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||
void ffi_zx_document_ffi_rust_future_complete_void(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_ACK_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_ACK_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_ack_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_cleanup_stale_sessions_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_clear_exported_events(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLOSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLOSE_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_close_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor_from_search(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE
|
||||
@ -586,6 +731,36 @@ uint16_t uniffi_zx_document_ffi_checksum_func_detect_material_type(void
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_extract_pdf_text_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_get_office_preview_config_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_IS_OFFICE_TYPE_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_IS_OFFICE_TYPE_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_is_office_type_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_MARK_EVENTS_FAILED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_MARK_EVENTS_FAILED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_mark_events_failed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN
|
||||
@ -598,36 +773,126 @@ uint16_t uniffi_zx_document_ffi_checksum_func_parse_markdown(void
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_parse_text(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PAUSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PAUSE_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_pause_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_HEARTBEAT_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_HEARTBEAT_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_heartbeat_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_marked_as_read_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_material_closed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_material_opened_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_position_changed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_reading_event(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_epub_chapters_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_METADATA_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_epub_metadata_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_image_meta(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_PDF_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_PDF_METADATA_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_pdf_metadata_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_text_stats(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_reload_stale_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_restore_position_from_anchor(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESUME_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESUME_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_resume_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_epub_chapters_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_markdown_blocks(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_PDF_PAGES
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_PDF_PAGES
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_pdf_pages(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_text_content(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_START_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_START_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_start_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION
|
||||
|
||||
4
bindings/ios/module.modulemap
Normal file
4
bindings/ios/module.modulemap
Normal file
@ -0,0 +1,4 @@
|
||||
framework module zx_documentFFI {
|
||||
header "zx_documentFFI.h"
|
||||
export *
|
||||
}
|
||||
910
bindings/ios/simulator/Headers/zx_documentFFI.h
Normal file
910
bindings/ios/simulator/Headers/zx_documentFFI.h
Normal file
@ -0,0 +1,910 @@
|
||||
// This file was autogenerated by some hot garbage in the `uniffi` crate.
|
||||
// Trust me, you don't want to mess with it!
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// The following structs are used to implement the lowest level
|
||||
// of the FFI, and thus useful to multiple uniffied crates.
|
||||
// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
|
||||
#ifdef UNIFFI_SHARED_H
|
||||
// We also try to prevent mixing versions of shared uniffi header structs.
|
||||
// If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4
|
||||
#ifndef UNIFFI_SHARED_HEADER_V4
|
||||
#error Combining helper code from multiple versions of uniffi is not supported
|
||||
#endif // ndef UNIFFI_SHARED_HEADER_V4
|
||||
#else
|
||||
#define UNIFFI_SHARED_H
|
||||
#define UNIFFI_SHARED_HEADER_V4
|
||||
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
|
||||
|
||||
typedef struct RustBuffer
|
||||
{
|
||||
uint64_t capacity;
|
||||
uint64_t len;
|
||||
uint8_t *_Nullable data;
|
||||
} RustBuffer;
|
||||
|
||||
typedef struct ForeignBytes
|
||||
{
|
||||
int32_t len;
|
||||
const uint8_t *_Nullable data;
|
||||
} ForeignBytes;
|
||||
|
||||
// Error definitions
|
||||
typedef struct RustCallStatus {
|
||||
int8_t code;
|
||||
RustBuffer errorBuf;
|
||||
} RustCallStatus;
|
||||
|
||||
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
|
||||
#endif // def UNIFFI_SHARED_H
|
||||
#ifndef UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK
|
||||
#define UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK
|
||||
typedef void (*UniffiRustFutureContinuationCallback)(uint64_t, int8_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK
|
||||
typedef void (*UniffiForeignFutureDroppedCallback)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE
|
||||
#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE
|
||||
typedef void (*UniffiCallbackInterfaceFree)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_CLONE
|
||||
#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_CLONE
|
||||
typedef uint64_t (*UniffiCallbackInterfaceClone)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK_STRUCT
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK_STRUCT
|
||||
typedef struct UniffiForeignFutureDroppedCallbackStruct {
|
||||
uint64_t handle;
|
||||
UniffiForeignFutureDroppedCallback _Nonnull free;
|
||||
} UniffiForeignFutureDroppedCallbackStruct;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U8
|
||||
typedef struct UniffiForeignFutureResultU8 {
|
||||
uint8_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU8;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8
|
||||
typedef void (*UniffiForeignFutureCompleteU8)(uint64_t, UniffiForeignFutureResultU8
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I8
|
||||
typedef struct UniffiForeignFutureResultI8 {
|
||||
int8_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI8;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8
|
||||
typedef void (*UniffiForeignFutureCompleteI8)(uint64_t, UniffiForeignFutureResultI8
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U16
|
||||
typedef struct UniffiForeignFutureResultU16 {
|
||||
uint16_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU16;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16
|
||||
typedef void (*UniffiForeignFutureCompleteU16)(uint64_t, UniffiForeignFutureResultU16
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I16
|
||||
typedef struct UniffiForeignFutureResultI16 {
|
||||
int16_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI16;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16
|
||||
typedef void (*UniffiForeignFutureCompleteI16)(uint64_t, UniffiForeignFutureResultI16
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U32
|
||||
typedef struct UniffiForeignFutureResultU32 {
|
||||
uint32_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32
|
||||
typedef void (*UniffiForeignFutureCompleteU32)(uint64_t, UniffiForeignFutureResultU32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I32
|
||||
typedef struct UniffiForeignFutureResultI32 {
|
||||
int32_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32
|
||||
typedef void (*UniffiForeignFutureCompleteI32)(uint64_t, UniffiForeignFutureResultI32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U64
|
||||
typedef struct UniffiForeignFutureResultU64 {
|
||||
uint64_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64
|
||||
typedef void (*UniffiForeignFutureCompleteU64)(uint64_t, UniffiForeignFutureResultU64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I64
|
||||
typedef struct UniffiForeignFutureResultI64 {
|
||||
int64_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64
|
||||
typedef void (*UniffiForeignFutureCompleteI64)(uint64_t, UniffiForeignFutureResultI64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F32
|
||||
typedef struct UniffiForeignFutureResultF32 {
|
||||
float returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultF32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32
|
||||
typedef void (*UniffiForeignFutureCompleteF32)(uint64_t, UniffiForeignFutureResultF32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F64
|
||||
typedef struct UniffiForeignFutureResultF64 {
|
||||
double returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultF64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64
|
||||
typedef void (*UniffiForeignFutureCompleteF64)(uint64_t, UniffiForeignFutureResultF64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_RUST_BUFFER
|
||||
typedef struct UniffiForeignFutureResultRustBuffer {
|
||||
RustBuffer returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultRustBuffer;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER
|
||||
typedef void (*UniffiForeignFutureCompleteRustBuffer)(uint64_t, UniffiForeignFutureResultRustBuffer
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_VOID
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_VOID
|
||||
typedef struct UniffiForeignFutureResultVoid {
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultVoid;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID
|
||||
typedef void (*UniffiForeignFutureCompleteVoid)(uint64_t, UniffiForeignFutureResultVoid
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_ACK_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_ACK_EVENTS_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_ack_events_v2(RustBuffer event_ids, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_cleanup_stale_sessions_ffi(int64_t now_ms, int64_t max_age_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
void uniffi_zx_document_ffi_fn_func_clear_exported_events(uint32_t count, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLOSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLOSE_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_close_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor_from_search(RustBuffer material_id, RustBuffer result, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_detect_material_type(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events_v2(uint32_t limit, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_extract_pdf_text_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_get_office_preview_config_ffi(RustBuffer material_type, uint64_t file_size, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_IS_OFFICE_TYPE_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_IS_OFFICE_TYPE_FFI
|
||||
int8_t uniffi_zx_document_ffi_fn_func_is_office_type_ffi(RustBuffer material_type, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_MARK_EVENTS_FAILED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_MARK_EVENTS_FAILED_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_mark_events_failed_v2(RustBuffer event_ids, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_parse_markdown(RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_TEXT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_TEXT
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_parse_text(RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PAUSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PAUSE_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_pause_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_HEARTBEAT_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_HEARTBEAT_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_heartbeat_v2(RustBuffer session_id, RustBuffer material_id, uint32_t active_seconds_delta, RustBuffer position, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_marked_as_read_v2(RustBuffer session_id, RustBuffer material_id, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_material_closed_v2(RustBuffer session_id, RustBuffer material_id, uint32_t active_seconds_delta, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_material_opened_v2(RustBuffer session_id, RustBuffer material_id, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_push_position_changed_v2(RustBuffer session_id, RustBuffer material_id, RustBuffer position, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||
void uniffi_zx_document_ffi_fn_func_push_reading_event(RustBuffer event, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_epub_chapters_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_METADATA_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_epub_metadata_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_image_meta(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_PDF_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_PDF_METADATA_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_pdf_metadata_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_text_stats(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
uint32_t uniffi_zx_document_ffi_fn_func_reload_stale_events_v2(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_restore_position_from_anchor(RustBuffer anchor, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESUME_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESUME_READING_SESSION_V2
|
||||
void uniffi_zx_document_ffi_fn_func_resume_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_epub_chapters_ffi(RustBuffer chapter_ids, RustBuffer chapter_texts, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_markdown_blocks(RustBuffer blocks, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_PDF_PAGES
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_PDF_PAGES
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_pdf_pages(RustBuffer page_numbers, RustBuffer page_texts, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_text_content(RustBuffer content, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_START_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_START_READING_SESSION_V2
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_start_reading_session_v2(RustBuffer material, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||
void uniffi_zx_document_ffi_fn_func_update_reading_position(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_ALLOC
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_ALLOC
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_alloc(uint64_t size, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FROM_BYTES
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FROM_BYTES
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FREE
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FREE
|
||||
void ffi_zx_document_ffi_rustbuffer_free(RustBuffer buf, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_RESERVE
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_RESERVE
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_reserve(RustBuffer buf, uint64_t additional, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U8
|
||||
void ffi_zx_document_ffi_rust_future_poll_u8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U8
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U8
|
||||
void ffi_zx_document_ffi_rust_future_free_u8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U8
|
||||
uint8_t ffi_zx_document_ffi_rust_future_complete_u8(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I8
|
||||
void ffi_zx_document_ffi_rust_future_poll_i8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I8
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I8
|
||||
void ffi_zx_document_ffi_rust_future_free_i8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I8
|
||||
int8_t ffi_zx_document_ffi_rust_future_complete_i8(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U16
|
||||
void ffi_zx_document_ffi_rust_future_poll_u16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U16
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U16
|
||||
void ffi_zx_document_ffi_rust_future_free_u16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U16
|
||||
uint16_t ffi_zx_document_ffi_rust_future_complete_u16(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I16
|
||||
void ffi_zx_document_ffi_rust_future_poll_i16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I16
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I16
|
||||
void ffi_zx_document_ffi_rust_future_free_i16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I16
|
||||
int16_t ffi_zx_document_ffi_rust_future_complete_i16(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U32
|
||||
void ffi_zx_document_ffi_rust_future_poll_u32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U32
|
||||
void ffi_zx_document_ffi_rust_future_free_u32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U32
|
||||
uint32_t ffi_zx_document_ffi_rust_future_complete_u32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I32
|
||||
void ffi_zx_document_ffi_rust_future_poll_i32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I32
|
||||
void ffi_zx_document_ffi_rust_future_free_i32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I32
|
||||
int32_t ffi_zx_document_ffi_rust_future_complete_i32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U64
|
||||
void ffi_zx_document_ffi_rust_future_poll_u64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U64
|
||||
void ffi_zx_document_ffi_rust_future_free_u64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U64
|
||||
uint64_t ffi_zx_document_ffi_rust_future_complete_u64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I64
|
||||
void ffi_zx_document_ffi_rust_future_poll_i64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I64
|
||||
void ffi_zx_document_ffi_rust_future_free_i64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I64
|
||||
int64_t ffi_zx_document_ffi_rust_future_complete_i64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F32
|
||||
void ffi_zx_document_ffi_rust_future_poll_f32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_f32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F32
|
||||
void ffi_zx_document_ffi_rust_future_free_f32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F32
|
||||
float ffi_zx_document_ffi_rust_future_complete_f32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F64
|
||||
void ffi_zx_document_ffi_rust_future_poll_f64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_f64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F64
|
||||
void ffi_zx_document_ffi_rust_future_free_f64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F64
|
||||
double ffi_zx_document_ffi_rust_future_complete_f64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_poll_rust_buffer(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_cancel_rust_buffer(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_free_rust_buffer(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_RUST_BUFFER
|
||||
RustBuffer ffi_zx_document_ffi_rust_future_complete_rust_buffer(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_VOID
|
||||
void ffi_zx_document_ffi_rust_future_poll_void(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_VOID
|
||||
void ffi_zx_document_ffi_rust_future_cancel_void(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_VOID
|
||||
void ffi_zx_document_ffi_rust_future_free_void(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||
void ffi_zx_document_ffi_rust_future_complete_void(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_ACK_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_ACK_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_ack_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEANUP_STALE_SESSIONS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_cleanup_stale_sessions_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_clear_exported_events(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLOSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLOSE_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_close_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor_from_search(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_detect_material_type(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXTRACT_PDF_TEXT_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_extract_pdf_text_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_get_office_preview_config_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_IS_OFFICE_TYPE_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_IS_OFFICE_TYPE_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_is_office_type_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_MARK_EVENTS_FAILED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_MARK_EVENTS_FAILED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_mark_events_failed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_parse_markdown(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_parse_text(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PAUSE_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PAUSE_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_pause_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_HEARTBEAT_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_HEARTBEAT_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_heartbeat_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MARKED_AS_READ_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_marked_as_read_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_CLOSED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_material_closed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_OPENED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_material_opened_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_POSITION_CHANGED_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_position_changed_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_reading_event(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_CHAPTERS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_epub_chapters_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_METADATA_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_epub_metadata_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_image_meta(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_PDF_METADATA_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_PDF_METADATA_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_pdf_metadata_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_text_stats(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RELOAD_STALE_EVENTS_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_reload_stale_events_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESTORE_POSITION_FROM_ANCHOR
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_restore_position_from_anchor(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESUME_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESUME_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_resume_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_EPUB_CHAPTERS_FFI
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_epub_chapters_ffi(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_markdown_blocks(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_PDF_PAGES
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_PDF_PAGES
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_pdf_pages(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_text_content(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_START_READING_SESSION_V2
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_START_READING_SESSION_V2
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_start_reading_session_v2(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_update_reading_position(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_UNIFFI_CONTRACT_VERSION
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_UNIFFI_CONTRACT_VERSION
|
||||
uint32_t ffi_zx_document_ffi_uniffi_contract_version(void
|
||||
|
||||
);
|
||||
#endif
|
||||
|
||||
4
bindings/ios/simulator/Modules/module.modulemap
Normal file
4
bindings/ios/simulator/Modules/module.modulemap
Normal file
@ -0,0 +1,4 @@
|
||||
framework module zx_documentFFI {
|
||||
header "zx_documentFFI.h"
|
||||
export *
|
||||
}
|
||||
Binary file not shown.
@ -15,9 +15,16 @@ fn main() {
|
||||
}
|
||||
Some("fixtures") => {
|
||||
println!("fixtures/");
|
||||
println!(" markdown/sample.md — all block types");
|
||||
println!(" text/sample.txt — multi-paragraph plain text");
|
||||
println!(" images/test-red.png — 1×1 red pixel PNG");
|
||||
println!(" markdown/sample.md — all block types");
|
||||
println!(" markdown/large_markdown.md — 200-heading stress test");
|
||||
println!(" text/sample.txt — multi-paragraph plain text");
|
||||
println!(" text/large_text.txt — 500-line stress test");
|
||||
println!(" images/test-red.png — 1×1 red pixel PNG");
|
||||
println!(" pdf/text_pdf.pdf — minimal 2-page PDF");
|
||||
println!(" pdf/scanned_pdf.pdf — minimal 1-page PDF");
|
||||
println!(" epub/simple.epub — 1-chapter EPUB");
|
||||
println!(" epub/epub_with_toc.epub — 3-chapter EPUB with NCX TOC");
|
||||
println!(" invalid_file.bin — 1KB random bytes");
|
||||
}
|
||||
Some("verify-ios") => {
|
||||
let root = project_root();
|
||||
|
||||
@ -10,6 +10,7 @@ infer = "0.16"
|
||||
mime_guess = "2"
|
||||
comrak = "0.29"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
zip = "2"
|
||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif"] }
|
||||
uniffi = "0.31"
|
||||
|
||||
|
||||
@ -1,71 +1,188 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::progress::ReadingPosition;
|
||||
use crate::search::SearchResult;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, uniffi::Enum)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum NoteAnchor {
|
||||
Material {
|
||||
material_id: String,
|
||||
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||
position_snapshot: Option<ReadingPosition>,
|
||||
},
|
||||
MarkdownBlock {
|
||||
material_id: String,
|
||||
block_id: String,
|
||||
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||
position_snapshot: Option<ReadingPosition>,
|
||||
},
|
||||
TextLine {
|
||||
material_id: String,
|
||||
line_number: u32,
|
||||
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||
position_snapshot: Option<ReadingPosition>,
|
||||
},
|
||||
PdfPage {
|
||||
material_id: String,
|
||||
page_number: u32,
|
||||
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||
position_snapshot: Option<ReadingPosition>,
|
||||
},
|
||||
Image {
|
||||
material_id: String,
|
||||
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||
position_snapshot: Option<ReadingPosition>,
|
||||
},
|
||||
EpubChapter {
|
||||
material_id: String,
|
||||
chapter_id: String,
|
||||
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||
position_snapshot: Option<ReadingPosition>,
|
||||
},
|
||||
KnowledgeItem {
|
||||
knowledge_item_id: String,
|
||||
},
|
||||
SearchResultAnchor {
|
||||
material_id: String,
|
||||
block_id: Option<String>,
|
||||
line_number: Option<u32>,
|
||||
page_number: Option<u32>,
|
||||
chapter_id: Option<String>,
|
||||
snippet: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl NoteAnchor {
|
||||
/// Create a NoteAnchor from a material_id and optional ReadingPosition.
|
||||
pub fn from_position(material_id: &str, position: Option<&crate::progress::ReadingPosition>) -> Self {
|
||||
pub fn from_position(material_id: &str, position: Option<&ReadingPosition>) -> Self {
|
||||
match position {
|
||||
Some(crate::progress::ReadingPosition::Markdown { block_id, .. }) => {
|
||||
Some(pos @ ReadingPosition::Markdown { block_id, .. }) => {
|
||||
NoteAnchor::MarkdownBlock {
|
||||
material_id: material_id.to_string(),
|
||||
block_id: block_id.clone(),
|
||||
position_snapshot: Some(pos.clone()),
|
||||
}
|
||||
}
|
||||
Some(crate::progress::ReadingPosition::Text { line_number, .. }) => {
|
||||
Some(pos @ ReadingPosition::Text { line_number, .. }) => {
|
||||
NoteAnchor::TextLine {
|
||||
material_id: material_id.to_string(),
|
||||
line_number: *line_number,
|
||||
position_snapshot: Some(pos.clone()),
|
||||
}
|
||||
}
|
||||
Some(crate::progress::ReadingPosition::Pdf { page_number, .. }) => {
|
||||
Some(pos @ ReadingPosition::Pdf { page_number, .. }) => {
|
||||
NoteAnchor::PdfPage {
|
||||
material_id: material_id.to_string(),
|
||||
page_number: *page_number,
|
||||
position_snapshot: Some(pos.clone()),
|
||||
}
|
||||
}
|
||||
Some(crate::progress::ReadingPosition::Image { .. }) => NoteAnchor::Image {
|
||||
Some(pos @ ReadingPosition::Image { .. }) => NoteAnchor::Image {
|
||||
material_id: material_id.to_string(),
|
||||
position_snapshot: Some(pos.clone()),
|
||||
},
|
||||
Some(crate::progress::ReadingPosition::Epub { chapter_id, .. }) => {
|
||||
Some(pos @ ReadingPosition::Epub { chapter_id, .. }) => {
|
||||
NoteAnchor::EpubChapter {
|
||||
material_id: material_id.to_string(),
|
||||
chapter_id: chapter_id.clone(),
|
||||
position_snapshot: Some(pos.clone()),
|
||||
}
|
||||
}
|
||||
_ => NoteAnchor::Material {
|
||||
material_id: material_id.to_string(),
|
||||
position_snapshot: position.cloned(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_search_result(material_id: &str, result: &SearchResult) -> Self {
|
||||
NoteAnchor::SearchResultAnchor {
|
||||
material_id: material_id.to_string(),
|
||||
block_id: Some(result.block_id.clone()),
|
||||
line_number: result.line_number,
|
||||
page_number: result.page_number,
|
||||
chapter_id: result.chapter_id.clone(),
|
||||
snippet: result.snippet.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore a ReadingPosition from this anchor.
|
||||
/// Uses position_snapshot if available; otherwise constructs a minimal position
|
||||
/// from the anchor fields. Returns None for anchors without position info.
|
||||
pub fn to_position(&self) -> Option<ReadingPosition> {
|
||||
match self {
|
||||
NoteAnchor::MarkdownBlock { block_id, position_snapshot, .. } => {
|
||||
position_snapshot.clone().or_else(|| {
|
||||
Some(ReadingPosition::Markdown {
|
||||
block_id: block_id.clone(),
|
||||
scroll_progress: 0.0,
|
||||
})
|
||||
})
|
||||
}
|
||||
NoteAnchor::TextLine { line_number, position_snapshot, .. } => {
|
||||
position_snapshot.clone().or_else(|| {
|
||||
Some(ReadingPosition::Text {
|
||||
line_number: *line_number,
|
||||
scroll_progress: 0.0,
|
||||
})
|
||||
})
|
||||
}
|
||||
NoteAnchor::PdfPage { page_number, position_snapshot, .. } => {
|
||||
position_snapshot.clone().or_else(|| {
|
||||
Some(ReadingPosition::Pdf {
|
||||
page_number: *page_number,
|
||||
page_progress: 0.0,
|
||||
overall_progress: 0.0,
|
||||
})
|
||||
})
|
||||
}
|
||||
NoteAnchor::Image { position_snapshot, .. } => position_snapshot.clone(),
|
||||
NoteAnchor::EpubChapter { chapter_id, position_snapshot, .. } => {
|
||||
position_snapshot.clone().or_else(|| {
|
||||
Some(ReadingPosition::Epub {
|
||||
chapter_id: chapter_id.clone(),
|
||||
chapter_progress: 0.0,
|
||||
overall_progress: 0.0,
|
||||
})
|
||||
})
|
||||
}
|
||||
NoteAnchor::Material { position_snapshot, .. } => position_snapshot.clone(),
|
||||
NoteAnchor::SearchResultAnchor {
|
||||
block_id,
|
||||
line_number,
|
||||
page_number,
|
||||
chapter_id,
|
||||
..
|
||||
} => {
|
||||
if let Some(pn) = page_number {
|
||||
Some(ReadingPosition::Pdf {
|
||||
page_number: *pn,
|
||||
page_progress: 0.0,
|
||||
overall_progress: 0.0,
|
||||
})
|
||||
} else if let Some(cid) = chapter_id {
|
||||
Some(ReadingPosition::Epub {
|
||||
chapter_id: cid.clone(),
|
||||
chapter_progress: 0.0,
|
||||
overall_progress: 0.0,
|
||||
})
|
||||
} else if let Some(ln) = line_number {
|
||||
Some(ReadingPosition::Text {
|
||||
line_number: *ln,
|
||||
scroll_progress: 0.0,
|
||||
})
|
||||
} else if let Some(bid) = block_id {
|
||||
Some(ReadingPosition::Markdown {
|
||||
block_id: bid.clone(),
|
||||
scroll_progress: 0.0,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
NoteAnchor::KnowledgeItem { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -76,35 +193,303 @@ mod tests {
|
||||
#[test]
|
||||
fn test_material_anchor() {
|
||||
let a = NoteAnchor::from_position("abc", None);
|
||||
assert_eq!(a, NoteAnchor::Material { material_id: "abc".into() });
|
||||
assert_eq!(
|
||||
a,
|
||||
NoteAnchor::Material {
|
||||
material_id: "abc".into(),
|
||||
position_snapshot: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_markdown_anchor() {
|
||||
let pos = ReadingPosition::Markdown { block_id: "h1".into(), scroll_progress: 0.5 };
|
||||
let pos = ReadingPosition::Markdown {
|
||||
block_id: "h1".into(),
|
||||
scroll_progress: 0.5,
|
||||
};
|
||||
let a = NoteAnchor::from_position("abc", Some(&pos));
|
||||
assert_eq!(a, NoteAnchor::MarkdownBlock { material_id: "abc".into(), block_id: "h1".into() });
|
||||
assert_eq!(
|
||||
a,
|
||||
NoteAnchor::MarkdownBlock {
|
||||
material_id: "abc".into(),
|
||||
block_id: "h1".into(),
|
||||
position_snapshot: Some(pos.clone()),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pdf_anchor() {
|
||||
let pos = ReadingPosition::Pdf { page_number: 3, page_progress: 0.5, overall_progress: 0.1 };
|
||||
let pos = ReadingPosition::Pdf {
|
||||
page_number: 3,
|
||||
page_progress: 0.5,
|
||||
overall_progress: 0.1,
|
||||
};
|
||||
let a = NoteAnchor::from_position("abc", Some(&pos));
|
||||
assert_eq!(a, NoteAnchor::PdfPage { material_id: "abc".into(), page_number: 3 });
|
||||
assert_eq!(
|
||||
a,
|
||||
NoteAnchor::PdfPage {
|
||||
material_id: "abc".into(),
|
||||
page_number: 3,
|
||||
position_snapshot: Some(pos.clone()),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anchor_serde() {
|
||||
let a = NoteAnchor::MarkdownBlock { material_id: "abc".into(), block_id: "h1".into() };
|
||||
let a = NoteAnchor::MarkdownBlock {
|
||||
material_id: "abc".into(),
|
||||
block_id: "h1".into(),
|
||||
position_snapshot: None,
|
||||
};
|
||||
let json = serde_json::to_string(&a).unwrap();
|
||||
let back: NoteAnchor = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(back, a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anchor_serde_with_snapshot() {
|
||||
let pos = ReadingPosition::Markdown {
|
||||
block_id: "h1".into(),
|
||||
scroll_progress: 0.5,
|
||||
};
|
||||
let a = NoteAnchor::MarkdownBlock {
|
||||
material_id: "abc".into(),
|
||||
block_id: "h1".into(),
|
||||
position_snapshot: Some(pos),
|
||||
};
|
||||
let json = serde_json::to_string(&a).unwrap();
|
||||
assert!(json.contains("\"positionSnapshot\""));
|
||||
let back: NoteAnchor = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(back, a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_position_falls_back_to_material() {
|
||||
let pos = ReadingPosition::Unknown;
|
||||
let a = NoteAnchor::from_position("abc", Some(&pos));
|
||||
assert_eq!(a, NoteAnchor::Material { material_id: "abc".into() });
|
||||
assert_eq!(
|
||||
a,
|
||||
NoteAnchor::Material {
|
||||
material_id: "abc".into(),
|
||||
position_snapshot: Some(ReadingPosition::Unknown),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_search_result_markdown() {
|
||||
let sr = SearchResult {
|
||||
block_id: "h2".into(),
|
||||
line_number: None,
|
||||
page_number: None,
|
||||
chapter_id: None,
|
||||
snippet: "…found text…".into(),
|
||||
match_start: 1,
|
||||
match_end: 10,
|
||||
};
|
||||
let a = NoteAnchor::from_search_result("mat1", &sr);
|
||||
match a {
|
||||
NoteAnchor::SearchResultAnchor {
|
||||
material_id,
|
||||
block_id,
|
||||
line_number,
|
||||
chapter_id,
|
||||
snippet,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(material_id, "mat1");
|
||||
assert_eq!(block_id, Some("h2".to_string()));
|
||||
assert_eq!(line_number, None);
|
||||
assert_eq!(chapter_id, None);
|
||||
assert_eq!(snippet, "…found text…");
|
||||
}
|
||||
_ => panic!("expected SearchResultAnchor"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_search_result_pdf() {
|
||||
let sr = SearchResult {
|
||||
block_id: "page-3".into(),
|
||||
line_number: None,
|
||||
page_number: Some(3),
|
||||
chapter_id: None,
|
||||
snippet: "…pdf hit…".into(),
|
||||
match_start: 0,
|
||||
match_end: 8,
|
||||
};
|
||||
let a = NoteAnchor::from_search_result("mat2", &sr);
|
||||
match a {
|
||||
NoteAnchor::SearchResultAnchor {
|
||||
material_id,
|
||||
page_number,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(material_id, "mat2");
|
||||
assert_eq!(page_number, Some(3));
|
||||
}
|
||||
_ => panic!("expected SearchResultAnchor"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_search_result_epub() {
|
||||
let sr = SearchResult {
|
||||
block_id: "ch2".into(),
|
||||
line_number: None,
|
||||
page_number: None,
|
||||
chapter_id: Some("ch2".into()),
|
||||
snippet: "…epub hit…".into(),
|
||||
match_start: 0,
|
||||
match_end: 9,
|
||||
};
|
||||
let a = NoteAnchor::from_search_result("mat3", &sr);
|
||||
match a {
|
||||
NoteAnchor::SearchResultAnchor {
|
||||
material_id,
|
||||
chapter_id,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(material_id, "mat3");
|
||||
assert_eq!(chapter_id, Some("ch2".to_string()));
|
||||
}
|
||||
_ => panic!("expected SearchResultAnchor"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_result_anchor_serde() {
|
||||
let a = NoteAnchor::SearchResultAnchor {
|
||||
material_id: "mat1".into(),
|
||||
block_id: Some("h1".into()),
|
||||
line_number: None,
|
||||
page_number: None,
|
||||
chapter_id: None,
|
||||
snippet: "…snippet…".into(),
|
||||
};
|
||||
let json = serde_json::to_string(&a).unwrap();
|
||||
assert!(json.contains("\"type\":\"SearchResultAnchor\""));
|
||||
let back: NoteAnchor = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(back, a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backward_compat_no_snapshot() {
|
||||
let old_json = r#"{"type":"MarkdownBlock","material_id":"abc","block_id":"h1"}"#;
|
||||
let a: NoteAnchor = serde_json::from_str(old_json).unwrap();
|
||||
assert_eq!(
|
||||
a,
|
||||
NoteAnchor::MarkdownBlock {
|
||||
material_id: "abc".into(),
|
||||
block_id: "h1".into(),
|
||||
position_snapshot: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_position_with_snapshot() {
|
||||
let pos = ReadingPosition::Markdown {
|
||||
block_id: "h1".into(),
|
||||
scroll_progress: 0.75,
|
||||
};
|
||||
let anchor = NoteAnchor::MarkdownBlock {
|
||||
material_id: "mat1".into(),
|
||||
block_id: "h1".into(),
|
||||
position_snapshot: Some(pos.clone()),
|
||||
};
|
||||
let restored = anchor.to_position();
|
||||
assert_eq!(restored, Some(pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_position_without_snapshot_uses_minimal() {
|
||||
let anchor = NoteAnchor::PdfPage {
|
||||
material_id: "mat1".into(),
|
||||
page_number: 5,
|
||||
position_snapshot: None,
|
||||
};
|
||||
let restored = anchor.to_position();
|
||||
assert_eq!(
|
||||
restored,
|
||||
Some(ReadingPosition::Pdf {
|
||||
page_number: 5,
|
||||
page_progress: 0.0,
|
||||
overall_progress: 0.0,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_position_from_search_result_pdf() {
|
||||
let anchor = NoteAnchor::SearchResultAnchor {
|
||||
material_id: "mat1".into(),
|
||||
block_id: Some("page-3".into()),
|
||||
line_number: None,
|
||||
page_number: Some(3),
|
||||
chapter_id: None,
|
||||
snippet: "…hit…".into(),
|
||||
};
|
||||
let restored = anchor.to_position();
|
||||
assert_eq!(
|
||||
restored,
|
||||
Some(ReadingPosition::Pdf {
|
||||
page_number: 3,
|
||||
page_progress: 0.0,
|
||||
overall_progress: 0.0,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_position_from_search_result_markdown() {
|
||||
let anchor = NoteAnchor::SearchResultAnchor {
|
||||
material_id: "mat1".into(),
|
||||
block_id: Some("h2".into()),
|
||||
line_number: None,
|
||||
page_number: None,
|
||||
chapter_id: None,
|
||||
snippet: "…hit…".into(),
|
||||
};
|
||||
let restored = anchor.to_position();
|
||||
assert_eq!(
|
||||
restored,
|
||||
Some(ReadingPosition::Markdown {
|
||||
block_id: "h2".into(),
|
||||
scroll_progress: 0.0,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_position_knowledge_item_returns_none() {
|
||||
let anchor = NoteAnchor::KnowledgeItem {
|
||||
knowledge_item_id: "ki1".into(),
|
||||
};
|
||||
assert_eq!(anchor.to_position(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_position_material_without_snapshot_returns_none() {
|
||||
let anchor = NoteAnchor::Material {
|
||||
material_id: "mat1".into(),
|
||||
position_snapshot: None,
|
||||
};
|
||||
assert_eq!(anchor.to_position(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip_position_anchor_position() {
|
||||
let pos = ReadingPosition::Epub {
|
||||
chapter_id: "ch3".into(),
|
||||
chapter_progress: 0.6,
|
||||
overall_progress: 0.3,
|
||||
};
|
||||
let anchor = NoteAnchor::from_position("mat1", Some(&pos));
|
||||
let restored = anchor.to_position();
|
||||
assert_eq!(restored, Some(pos));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,599 @@
|
||||
// M5: EPUB structure parsing (OPF, spine, nav, chapter list).
|
||||
// Rust Core will parse the EPUB container and expose chapter-level metadata.
|
||||
// Chapter HTML rendering is delegated to the host app's WebView / native HTML.
|
||||
// See #25 DOC-501 and #26 DOC-502 for design and implementation plan.
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::DocumentError;
|
||||
|
||||
/// Metadata extracted from an EPUB file.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct EpubMetadata {
|
||||
pub title: Option<String>,
|
||||
pub author: Option<String>,
|
||||
pub chapter_count: u32,
|
||||
pub file_size: u64,
|
||||
}
|
||||
|
||||
/// A single chapter in an EPUB spine.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct EpubChapter {
|
||||
pub chapter_id: String,
|
||||
pub title: String,
|
||||
/// Path within the EPUB zip, e.g. "OEBPS/chapter1.xhtml"
|
||||
pub path: String,
|
||||
/// Play order in the spine (0-based)
|
||||
pub play_order: u32,
|
||||
}
|
||||
|
||||
/// Combined result: metadata + chapters from a single EPUB open+parse.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EpubData {
|
||||
pub metadata: EpubMetadata,
|
||||
pub chapters: Vec<EpubChapter>,
|
||||
}
|
||||
|
||||
/// Read EPUB metadata and chapters in a single pass (single ZIP open + OPF parse).
|
||||
/// When both metadata and chapters are needed, prefer this over calling both separately.
|
||||
pub fn read_epub(file_path: &Path) -> Result<EpubData, DocumentError> {
|
||||
let file = fs::File::open(file_path).map_err(DocumentError::IoError)?;
|
||||
let file_size = file.metadata().map(|m| m.len()).unwrap_or(0);
|
||||
|
||||
let mut archive = zip::ZipArchive::new(file)
|
||||
.map_err(|e| DocumentError::ParseError(format!("invalid EPUB zip: {e}")))?;
|
||||
|
||||
let opf_xml = read_opf(&mut archive)?;
|
||||
let (title, author, spine_ids) = parse_opf(&opf_xml);
|
||||
let toc = read_toc(&mut archive, &opf_xml);
|
||||
|
||||
let metadata = EpubMetadata {
|
||||
title,
|
||||
author,
|
||||
chapter_count: spine_ids.len() as u32,
|
||||
file_size,
|
||||
};
|
||||
|
||||
let chapters: Vec<EpubChapter> = spine_ids
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, (id, href))| {
|
||||
let title = toc
|
||||
.iter()
|
||||
.find(|t| t.0 == *href || t.0 == *id)
|
||||
.map(|t| t.1.clone())
|
||||
.unwrap_or_else(|| format!("Chapter {}", i + 1));
|
||||
EpubChapter {
|
||||
chapter_id: id.clone(),
|
||||
title,
|
||||
path: href.clone(),
|
||||
play_order: i as u32,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(EpubData { metadata, chapters })
|
||||
}
|
||||
|
||||
/// Read EPUB metadata only. If chapters are also needed, use read_epub() instead.
|
||||
pub fn read_epub_metadata(file_path: &Path) -> Result<EpubMetadata, DocumentError> {
|
||||
read_epub(file_path).map(|data| data.metadata)
|
||||
}
|
||||
|
||||
/// Read EPUB chapter list. If metadata is also needed, use read_epub() instead.
|
||||
pub fn read_epub_chapters(file_path: &Path) -> Result<Vec<EpubChapter>, DocumentError> {
|
||||
read_epub(file_path).map(|data| data.chapters)
|
||||
}
|
||||
|
||||
// ── Internal helpers ──
|
||||
|
||||
fn read_opf(archive: &mut zip::ZipArchive<fs::File>) -> Result<String, DocumentError> {
|
||||
// 1. Read container.xml to find OPF path
|
||||
let container_xml = read_zip_entry(archive, "META-INF/container.xml")?;
|
||||
let opf_path = extract_container_rootfile(&container_xml)?;
|
||||
|
||||
// 2. Read OPF
|
||||
read_zip_entry(archive, &opf_path)
|
||||
}
|
||||
|
||||
fn read_zip_entry(
|
||||
archive: &mut zip::ZipArchive<fs::File>,
|
||||
name: &str,
|
||||
) -> Result<String, DocumentError> {
|
||||
let mut file = archive
|
||||
.by_name(name)
|
||||
.map_err(|_| DocumentError::ParseError(format!("EPUB missing: {name}")))?;
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf)
|
||||
.map_err(|e| DocumentError::IoError(e))?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn extract_container_rootfile(xml: &str) -> Result<String, DocumentError> {
|
||||
// <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>
|
||||
if let Some(start) = xml.find("full-path=") {
|
||||
let after = &xml[start + 10..];
|
||||
let delim = after.as_bytes()[0];
|
||||
let value_start = if delim == b'"' || delim == b'\'' { 1 } else { 0 };
|
||||
let value = &after[value_start..];
|
||||
if let Some(end) = value.find(if delim == b'"' || delim == b'\'' { delim as char } else { ' ' }) {
|
||||
return Ok(value[..end].to_string());
|
||||
}
|
||||
}
|
||||
Err(DocumentError::ParseError("EPUB container.xml: no rootfile found".into()))
|
||||
}
|
||||
|
||||
/// Returns (title, author, spine: Vec<(id, href)>)
|
||||
fn parse_opf(xml: &str) -> (Option<String>, Option<String>, Vec<(String, String)>) {
|
||||
let title = extract_tag_content(xml, "dc:title");
|
||||
let author = extract_tag_content(xml, "dc:creator");
|
||||
let manifest = extract_opf_manifest(xml);
|
||||
let spine = extract_opf_spine(xml);
|
||||
|
||||
// Map spine idrefs to manifest hrefs, skipping NAV items
|
||||
let nav_item = find_nav_item(xml);
|
||||
let spine_items: Vec<(String, String)> = spine
|
||||
.iter()
|
||||
.filter_map(|idref| {
|
||||
// Skip if this idref points to the NAV item
|
||||
if let Some((nav_id, _)) = &nav_item {
|
||||
if idref == nav_id {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
manifest
|
||||
.iter()
|
||||
.find(|(id, _href, _media)| id == idref)
|
||||
.map(|(id, href, _)| (id.clone(), href.clone()))
|
||||
})
|
||||
.collect();
|
||||
|
||||
(title, author, spine_items)
|
||||
}
|
||||
|
||||
fn extract_tag_content(xml: &str, tag: &str) -> Option<String> {
|
||||
let open = format!("<{}", tag);
|
||||
let pos = xml.find(&open)?;
|
||||
let after_tag = &xml[pos + open.len()..];
|
||||
// Skip attributes: find '>'
|
||||
let content_start = after_tag.find('>')? + 1;
|
||||
let rest = &after_tag[content_start..];
|
||||
let close = format!("</{}>", tag);
|
||||
let content_end = rest.find(&close)?;
|
||||
let content = rest[..content_end].trim().to_string();
|
||||
if content.is_empty() { None } else { Some(content) }
|
||||
}
|
||||
|
||||
fn extract_opf_manifest(xml: &str) -> Vec<(String, String, String)> {
|
||||
// Extract <item id="x" href="y" media-type="z"/>
|
||||
let mut items = Vec::new();
|
||||
let mut pos = 0;
|
||||
while let Some(i) = xml[pos..].find("<item ") {
|
||||
let start = pos + i;
|
||||
let end = xml[start..].find("/>").map(|e| start + e + 2)
|
||||
.or_else(|| xml[start..].find('>').map(|e| start + e + 1))
|
||||
.unwrap_or(start + 50);
|
||||
let tag_text = &xml[start..end.min(xml.len())];
|
||||
|
||||
let id = extract_attr(tag_text, "id");
|
||||
let href = extract_attr(tag_text, "href");
|
||||
let media = extract_attr(tag_text, "media-type");
|
||||
|
||||
if let (Some(id), Some(href)) = (id, href) {
|
||||
items.push((id, href, media.unwrap_or_default()));
|
||||
}
|
||||
pos = end;
|
||||
if pos >= xml.len() - 5 { break; }
|
||||
}
|
||||
items
|
||||
}
|
||||
|
||||
fn extract_opf_spine(xml: &str) -> Vec<String> {
|
||||
let mut items = Vec::new();
|
||||
let spine_start = xml.find("<spine").unwrap_or(0);
|
||||
let spine_end = xml[spine_start..].find("</spine>")
|
||||
.map(|e| spine_start + e + 9)
|
||||
.unwrap_or(xml.len());
|
||||
let spine_xml = &xml[spine_start..spine_end.min(xml.len())];
|
||||
|
||||
let mut pos = 0;
|
||||
while let Some(i) = spine_xml[pos..].find("<itemref") {
|
||||
let start = pos + i;
|
||||
let end = spine_xml[start..].find("/>").map(|e| start + e + 2)
|
||||
.or_else(|| spine_xml[start..].find('>').map(|e| start + e + 1))
|
||||
.unwrap_or(start + 30);
|
||||
let tag_text = &spine_xml[start..end.min(spine_xml.len())];
|
||||
if let Some(idref) = extract_attr(tag_text, "idref") {
|
||||
items.push(idref);
|
||||
}
|
||||
pos = end;
|
||||
if pos >= spine_xml.len() - 5 { break; }
|
||||
}
|
||||
items
|
||||
}
|
||||
|
||||
/// Read the table of contents. Tries NCX first, falls back to EPUB3 NAV.
|
||||
fn read_toc(
|
||||
archive: &mut zip::ZipArchive<fs::File>,
|
||||
opf_xml: &str,
|
||||
) -> Vec<(String, String)> {
|
||||
// Try NCX first (EPUB2)
|
||||
let toc = read_ncx_toc(archive, opf_xml);
|
||||
if !toc.is_empty() {
|
||||
return toc;
|
||||
}
|
||||
|
||||
// Fall back to EPUB3 NAV
|
||||
read_nav_toc(archive, opf_xml)
|
||||
}
|
||||
|
||||
/// Try to read NCX TOC; returns Vec<(src, title)>
|
||||
fn read_ncx_toc(
|
||||
archive: &mut zip::ZipArchive<fs::File>,
|
||||
opf_xml: &str,
|
||||
) -> Vec<(String, String)> {
|
||||
let ncx_href = extract_opf_manifest(opf_xml)
|
||||
.iter()
|
||||
.find(|(_id, href, media)| media == "application/x-dtbncx+xml" || href.ends_with(".ncx"))
|
||||
.map(|(_id, href, _)| href.clone());
|
||||
|
||||
match &ncx_href {
|
||||
Some(href) => match read_zip_entry(archive, href) {
|
||||
Ok(xml) => parse_ncx_navpoints(&xml),
|
||||
Err(_) => Vec::new(),
|
||||
},
|
||||
None => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try EPUB3 NAV document: find `<a href="...">title</a>` inside `<nav epub:type="toc">`.
|
||||
fn read_nav_toc(
|
||||
archive: &mut zip::ZipArchive<fs::File>,
|
||||
opf_xml: &str,
|
||||
) -> Vec<(String, String)> {
|
||||
// Find NAV href from OPF manifest item with properties="nav"
|
||||
let nav_href = find_nav_href_in_opf(opf_xml);
|
||||
|
||||
let nav_xml = match &nav_href {
|
||||
Some(href) => match read_zip_entry(archive, href) {
|
||||
Ok(xml) => xml,
|
||||
Err(_) => return Vec::new(),
|
||||
},
|
||||
None => return Vec::new(),
|
||||
};
|
||||
|
||||
parse_nav_links(&nav_xml)
|
||||
}
|
||||
|
||||
/// Find the NAV item (id, href) from the manifest, or None.
|
||||
fn find_nav_item(opf_xml: &str) -> Option<(String, String)> {
|
||||
let mut pos = 0;
|
||||
while let Some(i) = opf_xml[pos..].find("<item ") {
|
||||
let start = pos + i;
|
||||
let end = opf_xml[start..].find("/>").map(|e| start + e + 2)
|
||||
.or_else(|| opf_xml[start..].find('>').map(|e| start + e + 1))
|
||||
.unwrap_or(start + 200);
|
||||
let tag = &opf_xml[start..end.min(opf_xml.len())];
|
||||
|
||||
let is_nav = tag.contains("properties=\"nav\"")
|
||||
|| tag.contains("properties='nav'");
|
||||
if is_nav {
|
||||
if let (Some(id), Some(href)) = (extract_attr(tag, "id"), extract_attr(tag, "href")) {
|
||||
return Some((id, href));
|
||||
}
|
||||
}
|
||||
pos = end;
|
||||
if pos >= opf_xml.len() - 5 { break; }
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_nav_href_in_opf(opf_xml: &str) -> Option<String> {
|
||||
// Find <item ... properties="nav" ... /> and extract href
|
||||
let mut pos = 0;
|
||||
while let Some(i) = opf_xml[pos..].find("<item ") {
|
||||
let start = pos + i;
|
||||
let end = opf_xml[start..].find("/>").map(|e| start + e + 2)
|
||||
.or_else(|| opf_xml[start..].find('>').map(|e| start + e + 1))
|
||||
.unwrap_or(start + 200);
|
||||
let tag = &opf_xml[start..end.min(opf_xml.len())];
|
||||
|
||||
// Check for properties="nav" or properties='nav'
|
||||
let has_nav = tag.contains("properties=\"nav\"")
|
||||
|| tag.contains("properties='nav'")
|
||||
|| tag.contains("properties = \"nav\"");
|
||||
if has_nav {
|
||||
if let Some(href) = extract_attr(tag, "href") {
|
||||
return Some(href);
|
||||
}
|
||||
}
|
||||
pos = end;
|
||||
if pos >= opf_xml.len() - 5 { break; }
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_ncx_navpoints(xml: &str) -> Vec<(String, String)> {
|
||||
let mut items = Vec::new();
|
||||
let mut pos = 0;
|
||||
while let Some(i) = xml[pos..].find("<navPoint ") {
|
||||
let nav_start = pos + i;
|
||||
let nav_end = xml[nav_start..].find("</navPoint>")
|
||||
.map(|e| nav_start + e + 12)
|
||||
.unwrap_or(xml.len());
|
||||
let nav_xml = &xml[nav_start..nav_end.min(xml.len())];
|
||||
|
||||
let src = extract_ncx_src(nav_xml);
|
||||
let label = extract_tag_content(nav_xml, "text");
|
||||
|
||||
if let Some(src) = src {
|
||||
items.push((src, label.unwrap_or_default()));
|
||||
}
|
||||
|
||||
pos = nav_end;
|
||||
if pos >= xml.len() - 10 { break; }
|
||||
}
|
||||
items
|
||||
}
|
||||
|
||||
/// Parse `<a href="...">title</a>` links from EPUB3 NAV document.
|
||||
fn parse_nav_links(xml: &str) -> Vec<(String, String)> {
|
||||
let mut items = Vec::new();
|
||||
let mut pos = 0;
|
||||
|
||||
// Find each <a href="..."> element
|
||||
while let Some(i) = xml[pos..].find("<a ") {
|
||||
let start = pos + i;
|
||||
// Find end of <a> tag:
|
||||
let tag_end = xml[start..].find('>').map(|e| start + e + 1).unwrap_or(start + 50);
|
||||
let tag_text = &xml[start..tag_end.min(xml.len())];
|
||||
let href = extract_attr(tag_text, "href");
|
||||
|
||||
// Find text between <a ...> and </a>
|
||||
let content_start = tag_end;
|
||||
let content_end = xml[content_start..].find("</a>")
|
||||
.map(|e| content_start + e)
|
||||
.unwrap_or(content_start);
|
||||
let title = xml[content_start..content_end].trim().to_string();
|
||||
|
||||
if let (Some(href), _) = (href, &title) {
|
||||
if !title.is_empty() {
|
||||
items.push((href, title));
|
||||
}
|
||||
}
|
||||
|
||||
pos = content_end + 4; // skip past </a>
|
||||
if pos >= xml.len() - 5 { break; }
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
fn extract_ncx_src(nav_xml: &str) -> Option<String> {
|
||||
if let Some(i) = nav_xml.find("src=") {
|
||||
let after = &nav_xml[i + 4..];
|
||||
let delim = after.as_bytes()[0];
|
||||
let value_start = if delim == b'"' || delim == b'\'' { 1 } else { 0 };
|
||||
let value = &after[value_start..];
|
||||
if let Some(end) = value.find(if delim == b'"' || delim == b'\'' { delim as char } else { ' ' }) {
|
||||
return Some(value[..end].to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn extract_attr(tag_text: &str, attr_name: &str) -> Option<String> {
|
||||
let search = format!("{}=", attr_name);
|
||||
let pos = tag_text.find(&search)?;
|
||||
let after = &tag_text[pos + search.len()..];
|
||||
let delim = after.as_bytes().first().copied().unwrap_or(b'"');
|
||||
let value_start = if delim == b'"' || delim == b'\'' { 1 } else { 0 };
|
||||
let value = &after[value_start..];
|
||||
let end = value.find(if delim == b'"' || delim == b'\'' { delim as char } else { ' ' })?;
|
||||
Some(value[..end].to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Write;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Build a minimal valid EPUB zip in memory.
|
||||
fn minimal_epub(title: &str, author: &str, chapters: &[&str]) -> Vec<u8> {
|
||||
let mut buf = std::io::Cursor::new(Vec::new());
|
||||
{
|
||||
let mut zip = zip::ZipWriter::new(&mut buf);
|
||||
|
||||
let opts = zip::write::SimpleFileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Stored);
|
||||
|
||||
// mimetype (must be first, uncompressed)
|
||||
zip.start_file("mimetype", opts).unwrap();
|
||||
zip.write_all(b"application/epub+zip").unwrap();
|
||||
|
||||
// container.xml
|
||||
zip.start_file("META-INF/container.xml", opts).unwrap();
|
||||
zip.write_all(
|
||||
b"<?xml version=\"1.0\"?>\n<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n <rootfiles>\n <rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/>\n </rootfiles>\n</container>"
|
||||
).unwrap();
|
||||
|
||||
// OPF with metadata and spine
|
||||
let mut opf = format!(
|
||||
"<?xml version=\"1.0\"?>\n<package xmlns=\"http://www.idpf.org/2007/opf\" version=\"2.0\">\n<metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n <dc:title>{}</dc:title>\n <dc:creator>{}</dc:creator>\n</metadata>\n<manifest>\n <item id=\"ncx\" href=\"toc.ncx\" media-type=\"application/x-dtbncx+xml\"/>\n",
|
||||
title, author
|
||||
);
|
||||
for (i, ch) in chapters.iter().enumerate() {
|
||||
opf.push_str(&format!(
|
||||
" <item id=\"ch{}\" href=\"{}\" media-type=\"application/xhtml+xml\"/>\n",
|
||||
i, ch
|
||||
));
|
||||
}
|
||||
opf.push_str("</manifest>\n<spine toc=\"ncx\">\n");
|
||||
for i in 0..chapters.len() {
|
||||
opf.push_str(&format!(" <itemref idref=\"ch{}\"/>\n", i));
|
||||
}
|
||||
opf.push_str("</spine>\n</package>");
|
||||
|
||||
zip.start_file("OEBPS/content.opf", opts).unwrap();
|
||||
zip.write_all(opf.as_bytes()).unwrap();
|
||||
|
||||
// NCX
|
||||
let mut ncx = String::from(
|
||||
"<?xml version=\"1.0\"?>\n<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\">\n<navMap>\n"
|
||||
);
|
||||
for (i, ch) in chapters.iter().enumerate() {
|
||||
ncx.push_str(&format!(
|
||||
" <navPoint id=\"nav{}\" playOrder=\"{}\">\n <navLabel><text>Chapter {}</text></navLabel>\n <content src=\"{}\"/>\n </navPoint>\n",
|
||||
i, i + 1, i + 1, ch
|
||||
));
|
||||
}
|
||||
ncx.push_str("</navMap>\n</ncx>");
|
||||
|
||||
zip.start_file("toc.ncx", opts).unwrap();
|
||||
zip.write_all(ncx.as_bytes()).unwrap();
|
||||
|
||||
// Chapter files (empty)
|
||||
for ch in chapters {
|
||||
zip.start_file(*ch, opts).unwrap();
|
||||
zip.write_all(b"<html/>").unwrap();
|
||||
}
|
||||
|
||||
zip.finish().unwrap();
|
||||
}
|
||||
buf.into_inner()
|
||||
}
|
||||
|
||||
fn write_temp(data: &[u8], name: &str) -> std::path::PathBuf {
|
||||
let dir = std::env::temp_dir().join("zx_doc_epub_test");
|
||||
std::fs::create_dir_all(&dir).ok();
|
||||
let path = dir.join(name);
|
||||
std::fs::write(&path, data).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_epub_metadata() {
|
||||
let data = minimal_epub("Test Book", "Author Name", &["OEBPS/ch1.xhtml", "OEBPS/ch2.xhtml", "OEBPS/ch3.xhtml"]);
|
||||
let path = write_temp(&data, "test.epub");
|
||||
let meta = read_epub_metadata(&path).unwrap();
|
||||
assert_eq!(meta.title, Some("Test Book".into()));
|
||||
assert_eq!(meta.author, Some("Author Name".into()));
|
||||
assert_eq!(meta.chapter_count, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_epub_chapters() {
|
||||
let data = minimal_epub("Book", "Author", &["OEBPS/intro.xhtml", "OEBPS/ch1.xhtml"]);
|
||||
let path = write_temp(&data, "test_chapters.epub");
|
||||
let chapters = read_epub_chapters(&path).unwrap();
|
||||
assert_eq!(chapters.len(), 2);
|
||||
assert_eq!(chapters[0].chapter_id, "ch0");
|
||||
assert_eq!(chapters[0].title, "Chapter 1");
|
||||
assert_eq!(chapters[0].path, "OEBPS/intro.xhtml");
|
||||
assert_eq!(chapters[1].play_order, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_epub_empty() {
|
||||
let data = minimal_epub("Empty", "Author", &[]);
|
||||
let path = write_temp(&data, "empty.epub");
|
||||
let meta = read_epub_metadata(&path).unwrap();
|
||||
assert_eq!(meta.chapter_count, 0);
|
||||
let chapters = read_epub_chapters(&path).unwrap();
|
||||
assert!(chapters.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fixture_simple_epub() {
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent().unwrap().parent().unwrap();
|
||||
let path = root.join("fixtures/epub/simple.epub");
|
||||
if path.exists() {
|
||||
let meta = read_epub_metadata(&path).unwrap();
|
||||
assert_eq!(meta.title, Some("Simple Book".into()));
|
||||
assert_eq!(meta.chapter_count, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fixture_epub_with_toc() {
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent().unwrap().parent().unwrap();
|
||||
let path = root.join("fixtures/epub/epub_with_toc.epub");
|
||||
if path.exists() {
|
||||
let chapters = read_epub_chapters(&path).unwrap();
|
||||
assert_eq!(chapters.len(), 3);
|
||||
assert_eq!(chapters[0].title, "Chapter 1");
|
||||
}
|
||||
}
|
||||
|
||||
fn minimal_epub3(title: &str, chapters: &[&str]) -> Vec<u8> {
|
||||
let mut buf = std::io::Cursor::new(Vec::new());
|
||||
{
|
||||
let mut zip = zip::ZipWriter::new(&mut buf);
|
||||
let opts = zip::write::SimpleFileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Stored);
|
||||
|
||||
zip.start_file("mimetype", opts).unwrap();
|
||||
zip.write_all(b"application/epub+zip").unwrap();
|
||||
|
||||
zip.start_file("META-INF/container.xml", opts).unwrap();
|
||||
zip.write_all(
|
||||
b"<?xml version=\"1.0\"?>\n<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n <rootfiles>\n <rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/>\n </rootfiles>\n</container>"
|
||||
).unwrap();
|
||||
|
||||
// OPF with NAV instead of NCX
|
||||
let mut opf = format!(
|
||||
"<?xml version=\"1.0\"?>\n<package xmlns=\"http://www.idpf.org/2007/opf\" version=\"3.0\">\n<metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n <dc:title>{}</dc:title>\n</metadata>\n<manifest>\n <item id=\"nav\" href=\"nav.xhtml\" media-type=\"application/xhtml+xml\" properties=\"nav\"/>\n",
|
||||
title
|
||||
);
|
||||
for (i, ch) in chapters.iter().enumerate() {
|
||||
opf.push_str(&format!(
|
||||
" <item id=\"ch{}\" href=\"{}\" media-type=\"application/xhtml+xml\"/>\n",
|
||||
i, ch
|
||||
));
|
||||
}
|
||||
opf.push_str("</manifest>\n<spine>\n");
|
||||
for i in 0..chapters.len() {
|
||||
opf.push_str(&format!(" <itemref idref=\"ch{}\"/>\n", i));
|
||||
}
|
||||
opf.push_str("</spine>\n</package>");
|
||||
|
||||
zip.start_file("OEBPS/content.opf", opts).unwrap();
|
||||
zip.write_all(opf.as_bytes()).unwrap();
|
||||
|
||||
// NAV XHTML with <nav epub:type="toc">
|
||||
let mut nav = String::from("<?xml version=\"1.0\"?>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n<body>\n<nav epub:type=\"toc\">\n<ol>\n");
|
||||
for (i, ch) in chapters.iter().enumerate() {
|
||||
nav.push_str(&format!(
|
||||
" <li><a href=\"{}\">Chapter {}</a></li>\n",
|
||||
ch, i + 1
|
||||
));
|
||||
}
|
||||
nav.push_str("</ol>\n</nav>\n</body>\n</html>");
|
||||
|
||||
zip.start_file("nav.xhtml", opts).unwrap();
|
||||
zip.write_all(nav.as_bytes()).unwrap();
|
||||
|
||||
for ch in chapters {
|
||||
zip.start_file(*ch, opts).unwrap();
|
||||
zip.write_all(b"<html/>").unwrap();
|
||||
}
|
||||
|
||||
zip.finish().unwrap();
|
||||
}
|
||||
buf.into_inner()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_epub3_nav_fallback() {
|
||||
let data = minimal_epub3("EPUB3 Book", &["OEBPS/ch1.xhtml", "OEBPS/ch2.xhtml", "OEBPS/ch3.xhtml"]);
|
||||
let path = write_temp(&data, "test_epub3.epub");
|
||||
// No NCX — should fall back to NAV parsing
|
||||
let chapters = read_epub_chapters(&path).unwrap();
|
||||
assert_eq!(chapters.len(), 3);
|
||||
assert_eq!(chapters[0].title, "Chapter 1");
|
||||
assert_eq!(chapters[1].title, "Chapter 2");
|
||||
assert_eq!(chapters[2].title, "Chapter 3");
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,16 +238,25 @@ pub fn reload_stale_events_v2() -> u32 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::*;
|
||||
use crate::reading_material::ReadingMaterialRef;
|
||||
|
||||
static TEST_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
fn setup_session(material_id: &str) -> String {
|
||||
// Lock is held for the duration of each test via the caller.
|
||||
let mat = ReadingMaterialRef::new(material_id);
|
||||
let id = session_v2::start_reading_session_v2(mat, 1000).unwrap();
|
||||
clear_all_events_v2();
|
||||
id
|
||||
}
|
||||
|
||||
fn lock_test() -> std::sync::MutexGuard<'static, ()> {
|
||||
TEST_LOCK.lock().unwrap()
|
||||
}
|
||||
|
||||
fn teardown_session(id: &str) {
|
||||
let _ = session_v2::close_reading_session_v2(id);
|
||||
let _ = session_v2::remove_session_v2(id);
|
||||
@ -255,6 +264,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_push_opened() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_1");
|
||||
let ev = push_material_opened_v2(&sid, "mat_1", 1000).unwrap();
|
||||
assert_eq!(ev.event_type, ReadingEventTypeV2::MaterialOpened);
|
||||
@ -265,6 +275,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_push_closed() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_2");
|
||||
let ev = push_material_closed_v2(&sid, "mat_2", 0, 5000).unwrap();
|
||||
assert_eq!(ev.event_type, ReadingEventTypeV2::MaterialClosed);
|
||||
@ -273,6 +284,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_sequence_increments() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_seq");
|
||||
let e1 = push_material_opened_v2(&sid, "mat_seq", 1000).unwrap();
|
||||
// Use delta=0 for tracker-independent tests
|
||||
@ -286,6 +298,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_export_ack_flow() {
|
||||
let _guard = TEST_LOCK.lock().unwrap();
|
||||
clear_all_events_v2();
|
||||
let sid = setup_session("mat_ack");
|
||||
let e1 = push_material_opened_v2(&sid, "mat_ack", 1000).unwrap();
|
||||
@ -310,6 +323,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_mark_failed_and_retry() {
|
||||
let _guard = TEST_LOCK.lock().unwrap();
|
||||
clear_all_events_v2();
|
||||
let sid = setup_session("mat_retry");
|
||||
let e1 = push_heartbeat_v2(&sid, "mat_retry", 15, None, 1000).unwrap();
|
||||
@ -334,6 +348,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_buffer_size_not_exceeded() {
|
||||
let _guard = TEST_LOCK.lock().unwrap();
|
||||
clear_all_events_v2();
|
||||
let sid = setup_session("mat_overflow");
|
||||
let sz = buffer_size_v2();
|
||||
@ -348,6 +363,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_ack_nonexistent_no_crash() {
|
||||
let _guard = lock_test();
|
||||
clear_all_events_v2();
|
||||
let removed = ack_events_v2(&["nonexistent".to_string()]);
|
||||
assert_eq!(removed, 0);
|
||||
@ -355,6 +371,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_closed_session_rejects() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_reject");
|
||||
session_v2::close_reading_session_v2(&sid).unwrap();
|
||||
assert!(push_heartbeat_v2(&sid, "mat_reject", 15, None, 3000).is_err());
|
||||
@ -363,6 +380,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_serde_roundtrip() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_serde");
|
||||
let ev = push_material_opened_v2(&sid, "mat_serde", 1000).unwrap();
|
||||
let json = serde_json::to_string(&ev).unwrap();
|
||||
@ -371,4 +389,222 @@ mod tests {
|
||||
assert_eq!(back.event_id, ev.event_id);
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_position_changed() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_pc");
|
||||
let pos = ReadingPosition::Markdown { block_id: "h1".into(), scroll_progress: 0.5 };
|
||||
let ev = push_position_changed_v2(&sid, "mat_pc", pos.clone(), 2000).unwrap();
|
||||
assert_eq!(ev.event_type, ReadingEventTypeV2::PositionChanged);
|
||||
assert_eq!(ev.active_seconds_delta, 0);
|
||||
assert_eq!(ev.material_id, "mat_pc");
|
||||
assert!(ev.position.is_some());
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_heartbeat_with_position() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_hb");
|
||||
let pos = ReadingPosition::Pdf { page_number: 3, page_progress: 0.5, overall_progress: 0.1 };
|
||||
let ev = push_heartbeat_v2(&sid, "mat_hb", 30, Some(pos.clone()), 3000).unwrap();
|
||||
assert_eq!(ev.event_type, ReadingEventTypeV2::Heartbeat);
|
||||
assert_eq!(ev.active_seconds_delta, 30);
|
||||
assert_eq!(ev.material_id, "mat_hb");
|
||||
assert!(ev.position.is_some());
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_heartbeat_without_position() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_hb2");
|
||||
let ev = push_heartbeat_v2(&sid, "mat_hb2", 10, None, 3000).unwrap();
|
||||
assert_eq!(ev.event_type, ReadingEventTypeV2::Heartbeat);
|
||||
assert_eq!(ev.active_seconds_delta, 10);
|
||||
assert!(ev.position.is_none());
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_marked_as_read() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_mar");
|
||||
let ev = push_marked_as_read_v2(&sid, "mat_mar", 4000).unwrap();
|
||||
assert_eq!(ev.event_type, ReadingEventTypeV2::MarkedAsRead);
|
||||
assert_eq!(ev.active_seconds_delta, 0);
|
||||
assert_eq!(ev.material_id, "mat_mar");
|
||||
assert!(ev.position.is_none());
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_event_id_is_non_empty() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_id");
|
||||
let ev = push_material_opened_v2(&sid, "mat_id", 1000).unwrap();
|
||||
assert!(!ev.event_id.is_empty());
|
||||
assert_eq!(ev.event_id.len(), 36); // UUID v4 standard
|
||||
assert!(ev.event_id.contains('-'));
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_event_ids_are_unique() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_uniq");
|
||||
let e1 = push_material_opened_v2(&sid, "mat_uniq", 1000).unwrap();
|
||||
let e2 = push_position_changed_v2(&sid, "mat_uniq", ReadingPosition::Unknown, 2000).unwrap();
|
||||
assert_ne!(e1.event_id, e2.event_id);
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_client_session_id_present() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_csid");
|
||||
let ev = push_material_opened_v2(&sid, "mat_csid", 1000).unwrap();
|
||||
assert_eq!(ev.client_session_id, sid);
|
||||
assert!(!ev.client_session_id.is_empty());
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timestamp_ms_set() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_ts");
|
||||
let ev = push_material_opened_v2(&sid, "mat_ts", 123456789).unwrap();
|
||||
assert_eq!(ev.timestamp_ms, 123456789);
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_normalized_in_event() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_norm");
|
||||
let pos = ReadingPosition::Pdf { page_number: 1, page_progress: 2.5, overall_progress: -0.5 };
|
||||
let ev = push_position_changed_v2(&sid, "mat_norm", pos, 1000).unwrap();
|
||||
let p = ev.position.unwrap();
|
||||
match p {
|
||||
ReadingPosition::Pdf { page_progress, overall_progress, .. } => {
|
||||
assert_eq!(page_progress, 1.0); // clamped from 2.5
|
||||
assert_eq!(overall_progress, 0.0); // clamped from -0.5
|
||||
}
|
||||
_ => panic!("expected Pdf position"),
|
||||
}
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_five_event_types_have_distinct_types() {
|
||||
let _guard = lock_test();
|
||||
let sid = setup_session("mat_types");
|
||||
let e1 = push_material_opened_v2(&sid, "mat_types", 1000).unwrap();
|
||||
let e2 = push_material_closed_v2(&sid, "mat_types", 0, 2000).unwrap();
|
||||
// Need new session for more events since session is now closed
|
||||
teardown_session(&sid);
|
||||
|
||||
let sid2 = setup_session("mat_types2");
|
||||
let e3 = push_position_changed_v2(&sid2, "mat_types2", ReadingPosition::Unknown, 1000).unwrap();
|
||||
let e4 = push_heartbeat_v2(&sid2, "mat_types2", 10, None, 2000).unwrap();
|
||||
let e5 = push_marked_as_read_v2(&sid2, "mat_types2", 3000).unwrap();
|
||||
|
||||
assert_eq!(e1.event_type, ReadingEventTypeV2::MaterialOpened);
|
||||
assert_eq!(e2.event_type, ReadingEventTypeV2::MaterialClosed);
|
||||
assert_eq!(e3.event_type, ReadingEventTypeV2::PositionChanged);
|
||||
assert_eq!(e4.event_type, ReadingEventTypeV2::Heartbeat);
|
||||
assert_eq!(e5.event_type, ReadingEventTypeV2::MarkedAsRead);
|
||||
teardown_session(&sid2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reload_stale_events() {
|
||||
let _guard = TEST_LOCK.lock().unwrap();
|
||||
clear_all_events_v2();
|
||||
let sid = setup_session("mat_stale");
|
||||
let e1 = push_heartbeat_v2(&sid, "mat_stale", 15, None, 1000).unwrap();
|
||||
let exported = export_pending_events_v2(100, 2000);
|
||||
assert!(exported.iter().any(|e| e.event_id == e1.event_id));
|
||||
let reloaded = reload_stale_events_v2();
|
||||
assert_eq!(reloaded, 1);
|
||||
let reexported = export_pending_events_v2(100, 3000);
|
||||
assert!(reexported.iter().any(|e| e.event_id == e1.event_id));
|
||||
ack_events_v2(&[e1.event_id.clone()]);
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_buffer_lifecycle() {
|
||||
let _guard = TEST_LOCK.lock().unwrap();
|
||||
clear_all_events_v2();
|
||||
let sid = setup_session("mat_lifecycle");
|
||||
|
||||
// Step 1: push 2 events
|
||||
let e1 = push_material_opened_v2(&sid, "mat_lifecycle", 1000).unwrap();
|
||||
let e2 = push_heartbeat_v2(&sid, "mat_lifecycle", 15, None, 2000).unwrap();
|
||||
let sz = buffer_size_v2();
|
||||
assert!(sz >= 2, "buffer should have at least 2 events, got {sz}");
|
||||
|
||||
// Step 2: export → events marked Exported
|
||||
let batch1 = export_pending_events_v2(100, 3000);
|
||||
assert!(batch1.iter().any(|e| e.event_id == e1.event_id));
|
||||
assert!(batch1.iter().any(|e| e.event_id == e2.event_id));
|
||||
|
||||
// Step 3: export again → should NOT re-export (already Exported)
|
||||
let batch2 = export_pending_events_v2(100, 4000);
|
||||
let reexported = batch2.iter().any(|e| e.event_id == e1.event_id || e.event_id == e2.event_id);
|
||||
assert!(!reexported, "acked events should not be re-exported");
|
||||
|
||||
// Step 4: ack e1 only
|
||||
let acked = ack_events_v2(&[e1.event_id.clone()]);
|
||||
assert_eq!(acked, 1);
|
||||
|
||||
// Step 5: e2 still in buffer (unacked), e1 is gone
|
||||
// Mark e2 as failed (simulates upload failure)
|
||||
let marked = mark_events_failed_v2(&[e2.event_id.clone()]);
|
||||
assert_eq!(marked, 1);
|
||||
|
||||
// Step 6: retry → failed events are re-exportable
|
||||
let batch3 = export_pending_events_v2(100, 5000);
|
||||
assert!(batch3.iter().any(|e| e.event_id == e2.event_id),
|
||||
"failed event should be re-exported");
|
||||
assert!(!batch3.iter().any(|e| e.event_id == e1.event_id),
|
||||
"acked event should not appear");
|
||||
|
||||
// Step 7: ack e2 to clean up
|
||||
ack_events_v2(&[e2.event_id.clone()]);
|
||||
|
||||
teardown_session(&sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer_limit_export() {
|
||||
let _guard = TEST_LOCK.lock().unwrap();
|
||||
clear_all_events_v2();
|
||||
let sid = setup_session("mat_limit");
|
||||
|
||||
// Push 5 events
|
||||
let mut ids = Vec::new();
|
||||
for i in 0..5 {
|
||||
let e = push_material_opened_v2(&sid, "mat_limit", 1000 + i * 100).unwrap();
|
||||
ids.push(e.event_id);
|
||||
}
|
||||
|
||||
// Export with limit=2
|
||||
let batch = export_pending_events_v2(2, 5000);
|
||||
assert_eq!(batch.len(), 2);
|
||||
|
||||
// Next export should get the remaining 3
|
||||
let batch2 = export_pending_events_v2(10, 6000);
|
||||
let remaining = ids.len() as u32 - 2;
|
||||
assert!(batch2.len() >= remaining as usize,
|
||||
"expected at least {remaining} remaining, got {}", batch2.len());
|
||||
|
||||
// Clean up
|
||||
for e in [batch, batch2].concat() {
|
||||
ack_events_v2(&[e.event_id]);
|
||||
}
|
||||
teardown_session(&sid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ pub mod events_v2;
|
||||
pub mod image_meta;
|
||||
pub mod markdown;
|
||||
pub mod material_type;
|
||||
pub mod office;
|
||||
pub mod pdf;
|
||||
pub mod progress;
|
||||
pub mod reading_material;
|
||||
|
||||
@ -380,4 +380,85 @@ mod tests {
|
||||
assert!(blocks.iter().any(|b| matches!(b, DocumentBlock::List { .. })));
|
||||
assert!(blocks.iter().any(|b| matches!(b, DocumentBlock::CodeBlock { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fixture_complex_markdown() {
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent().unwrap().parent().unwrap();
|
||||
let path = root.join("fixtures/markdown/complex_markdown.md");
|
||||
let md = std::fs::read_to_string(&path).expect("fixture not found");
|
||||
let blocks = parse_markdown(&md).unwrap();
|
||||
assert!(!blocks.is_empty(), "should parse at least some blocks");
|
||||
|
||||
// Verify all 8 block types are present
|
||||
let has_heading = blocks.iter().any(|b| matches!(b, DocumentBlock::Heading { .. }));
|
||||
let has_para = blocks.iter().any(|b| matches!(b, DocumentBlock::Paragraph { .. }));
|
||||
let has_list = blocks.iter().any(|b| matches!(b, DocumentBlock::List { .. }));
|
||||
let has_code = blocks.iter().any(|b| matches!(b, DocumentBlock::CodeBlock { .. }));
|
||||
let has_quote = blocks.iter().any(|b| matches!(b, DocumentBlock::Quote { .. }));
|
||||
let has_table = blocks.iter().any(|b| matches!(b, DocumentBlock::Table { .. }));
|
||||
let has_image = blocks.iter().any(|b| matches!(b, DocumentBlock::Image { .. }));
|
||||
let has_rule = blocks.iter().any(|b| matches!(b, DocumentBlock::HorizontalRule { .. }));
|
||||
|
||||
assert!(has_heading, "missing heading");
|
||||
assert!(has_para, "missing paragraph");
|
||||
assert!(has_list, "missing list");
|
||||
assert!(has_code, "missing code block");
|
||||
assert!(has_quote, "missing quote");
|
||||
assert!(has_table, "missing table");
|
||||
assert!(has_image, "missing image");
|
||||
assert!(has_rule, "missing horizontal rule");
|
||||
|
||||
// Verify heading level
|
||||
let headings: Vec<_> = blocks.iter().filter_map(|b| {
|
||||
if let DocumentBlock::Heading { level, text, .. } = b {
|
||||
Some((*level, text.clone()))
|
||||
} else { None }
|
||||
}).collect();
|
||||
assert!(headings.iter().any(|(l, _)| *l == 1), "should have h1");
|
||||
assert!(headings.iter().any(|(l, _)| *l == 2), "should have h2");
|
||||
assert!(headings.iter().any(|(l, _)| *l == 3), "should have h3");
|
||||
|
||||
// Verify code block has language
|
||||
let code_blocks: Vec<_> = blocks.iter().filter_map(|b| {
|
||||
if let DocumentBlock::CodeBlock { language, code, .. } = b {
|
||||
Some((language.clone(), code.clone()))
|
||||
} else { None }
|
||||
}).collect();
|
||||
assert!(code_blocks.iter().any(|(lang, _)| lang.as_deref() == Some("rust")), "should have rust code block");
|
||||
|
||||
// Verify table structure
|
||||
for b in &blocks {
|
||||
if let DocumentBlock::Table { headers, rows, .. } = b {
|
||||
assert!(!headers.is_empty(), "table should have headers");
|
||||
assert!(!rows.is_empty(), "table should have rows");
|
||||
assert_eq!(headers.len(), rows[0].len(), "table row width should match headers");
|
||||
}
|
||||
}
|
||||
|
||||
// Verify image has src and alt
|
||||
for b in &blocks {
|
||||
if let DocumentBlock::Image { src, alt, .. } = b {
|
||||
assert!(!src.is_empty(), "image should have src");
|
||||
assert!(alt.is_some(), "image should have alt text");
|
||||
}
|
||||
}
|
||||
|
||||
// Verify block_ids are non-empty and unique
|
||||
let ids: Vec<&str> = blocks.iter().map(|b| match b {
|
||||
DocumentBlock::Heading { id, .. } => id.as_str(),
|
||||
DocumentBlock::Paragraph { id, .. } => id.as_str(),
|
||||
DocumentBlock::List { id, .. } => id.as_str(),
|
||||
DocumentBlock::CodeBlock { id, .. } => id.as_str(),
|
||||
DocumentBlock::Quote { id, .. } => id.as_str(),
|
||||
DocumentBlock::Table { id, .. } => id.as_str(),
|
||||
DocumentBlock::Image { id, .. } => id.as_str(),
|
||||
DocumentBlock::HorizontalRule { id, .. } => id.as_str(),
|
||||
}).collect();
|
||||
for id in &ids {
|
||||
assert!(!id.is_empty(), "block_id should not be empty");
|
||||
}
|
||||
let unique: std::collections::HashSet<_> = ids.iter().collect();
|
||||
assert_eq!(unique.len(), ids.len(), "all block_ids should be unique");
|
||||
}
|
||||
}
|
||||
|
||||
105
crates/zx_document_core/src/office.rs
Normal file
105
crates/zx_document_core/src/office.rs
Normal file
@ -0,0 +1,105 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::DocumentError;
|
||||
use crate::material_type::MaterialType;
|
||||
|
||||
/// Describes how an Office document should be previewed.
|
||||
///
|
||||
/// - Word/Excel → PlatformPreview (iOS: QLPreviewController / WKWebView)
|
||||
/// - PowerPoint → ExternalOpen (open in system app)
|
||||
/// - Future: ServerConvertedPdf (server converts Office→PDF)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Enum)]
|
||||
pub enum OfficePreviewStrategy {
|
||||
/// Use OS-native preview (QLPreviewController on iOS, Intent on Android)
|
||||
PlatformPreview,
|
||||
/// Open in external app (e.g., PowerPoint app for .pptx)
|
||||
ExternalOpen,
|
||||
/// Server-side conversion to PDF, then preview the PDF
|
||||
ServerConvertedPdf,
|
||||
/// Format not supported for preview
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
/// Preview configuration for an Office document.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct OfficePreviewConfig {
|
||||
pub material_type: MaterialType,
|
||||
pub strategy: OfficePreviewStrategy,
|
||||
pub file_size: u64,
|
||||
/// Whether the document can be searched after server conversion
|
||||
pub supports_search_after_conversion: bool,
|
||||
}
|
||||
|
||||
/// Get the recommended preview strategy for an Office material type.
|
||||
pub fn get_office_preview_config(
|
||||
material_type: &MaterialType,
|
||||
file_size: u64,
|
||||
) -> Result<OfficePreviewConfig, DocumentError> {
|
||||
let (strategy, supports_search) = match material_type {
|
||||
MaterialType::Word | MaterialType::Excel => {
|
||||
(OfficePreviewStrategy::PlatformPreview, true)
|
||||
}
|
||||
MaterialType::PowerPoint => {
|
||||
(OfficePreviewStrategy::ExternalOpen, true)
|
||||
}
|
||||
_ => {
|
||||
return Err(DocumentError::UnsupportedFormat(
|
||||
"not an Office document type".into()
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(OfficePreviewConfig {
|
||||
material_type: material_type.clone(),
|
||||
strategy,
|
||||
file_size,
|
||||
supports_search_after_conversion: supports_search,
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if a material type is an Office document.
|
||||
pub fn is_office_type(mt: &MaterialType) -> bool {
|
||||
matches!(
|
||||
mt,
|
||||
MaterialType::Word | MaterialType::Excel | MaterialType::PowerPoint
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_word_preview_strategy() {
|
||||
let config = get_office_preview_config(&MaterialType::Word, 1024).unwrap();
|
||||
assert!(matches!(config.strategy, OfficePreviewStrategy::PlatformPreview));
|
||||
assert!(config.supports_search_after_conversion);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_excel_preview_strategy() {
|
||||
let config = get_office_preview_config(&MaterialType::Excel, 2048).unwrap();
|
||||
assert!(matches!(config.strategy, OfficePreviewStrategy::PlatformPreview));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_powerpoint_preview_strategy() {
|
||||
let config = get_office_preview_config(&MaterialType::PowerPoint, 512).unwrap();
|
||||
assert!(matches!(config.strategy, OfficePreviewStrategy::ExternalOpen));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_office_rejected() {
|
||||
assert!(get_office_preview_config(&MaterialType::Markdown, 100).is_err());
|
||||
assert!(get_office_preview_config(&MaterialType::Pdf, 100).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_office_type() {
|
||||
assert!(is_office_type(&MaterialType::Word));
|
||||
assert!(is_office_type(&MaterialType::Excel));
|
||||
assert!(is_office_type(&MaterialType::PowerPoint));
|
||||
assert!(!is_office_type(&MaterialType::Pdf));
|
||||
assert!(!is_office_type(&MaterialType::Markdown));
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,298 @@
|
||||
// M4: PDF is handled via platform preview (iOS PDFKit / QuickLook, Android system).
|
||||
// Rust Core only provides the unified ReadingPosition model (see progress.rs).
|
||||
// Full PDF parsing (pdfium, text extraction) is deferred to a later milestone.
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::DocumentError;
|
||||
|
||||
const MAX_SCAN_BYTES: usize = 8 * 1024 * 1024;
|
||||
|
||||
/// Metadata extracted from a PDF file.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct PdfMetadata {
|
||||
pub page_count: u32,
|
||||
pub title: Option<String>,
|
||||
pub author: Option<String>,
|
||||
pub file_size: u64,
|
||||
}
|
||||
|
||||
/// A single page of extracted PDF text.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct PdfPageText {
|
||||
pub page_number: u32,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
/// Read PDF metadata using lightweight byte-level parsing.
|
||||
pub fn read_pdf_metadata(file_path: &Path) -> Result<PdfMetadata, DocumentError> {
|
||||
let data = fs::read(file_path)?;
|
||||
let file_size = data.len() as u64;
|
||||
let text = String::from_utf8_lossy(&data);
|
||||
|
||||
let page_count = count_pdf_pages(&text);
|
||||
let title = extract_pdf_info_field(&text, "/Title");
|
||||
let author = extract_pdf_info_field(&text, "/Author");
|
||||
|
||||
Ok(PdfMetadata { page_count, title, author, file_size })
|
||||
}
|
||||
|
||||
/// Extract text from PDF pages.
|
||||
/// Returns empty for now — full extraction requires pdfium or similar engine.
|
||||
pub fn extract_pdf_text(_file_path: &Path) -> Result<Vec<PdfPageText>, DocumentError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// Count pages by scanning for /Type /Page objects.
|
||||
fn count_pdf_pages(text: &str) -> u32 {
|
||||
let mut count = 0u32;
|
||||
let scan_limit = text.len().min(MAX_SCAN_BYTES);
|
||||
|
||||
let mut pos = 0;
|
||||
while pos < scan_limit {
|
||||
let type_start = match text[pos..].find("/Type") {
|
||||
Some(i) => pos + i,
|
||||
None => break,
|
||||
};
|
||||
|
||||
let after_type = type_start + 5;
|
||||
if after_type >= scan_limit {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(page_start) = text[after_type..].find("/Page") {
|
||||
let page_pos = after_type + page_start;
|
||||
let after_page = page_pos + 5;
|
||||
if after_page >= scan_limit {
|
||||
break;
|
||||
}
|
||||
let next_char = text.as_bytes()[after_page];
|
||||
if next_char.is_ascii_whitespace() || next_char == b'/' || next_char == b'>' {
|
||||
if page_pos == after_type
|
||||
|| (text.as_bytes()[page_pos - 1] != b's'
|
||||
&& text.as_bytes()[page_pos - 1] != b'S')
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
pos = after_page;
|
||||
} else {
|
||||
pos = after_type;
|
||||
}
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
count = count_pages_from_pages_dict(text);
|
||||
}
|
||||
if count == 0 && text.starts_with("%PDF-") {
|
||||
count = 1;
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
fn count_pages_from_pages_dict(text: &str) -> u32 {
|
||||
if let Some(pages_pos) = text.find("/Pages") {
|
||||
let region = &text[pages_pos..text.len().min(pages_pos + 2000)];
|
||||
if let Some(count_pos) = region.find("/Count") {
|
||||
let after_count = ®ion[count_pos + 6..];
|
||||
if let Some(num_str) = after_count
|
||||
.trim_start()
|
||||
.split(|c: char| !c.is_ascii_digit())
|
||||
.next()
|
||||
{
|
||||
if let Ok(n) = num_str.parse::<u32>() {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn extract_pdf_info_field(text: &str, field: &str) -> Option<String> {
|
||||
let field_pos = text.find(field)?;
|
||||
let after_field = &text[field_pos + field.len()..];
|
||||
let trimmed = after_field.trim_start();
|
||||
|
||||
// Parenthesized string: (value)
|
||||
if trimmed.starts_with('(') {
|
||||
let mut depth = 0;
|
||||
let mut result = String::new();
|
||||
let mut chars = trimmed.chars();
|
||||
while let Some(c) = chars.next() {
|
||||
if c == '(' {
|
||||
if depth > 0 {
|
||||
result.push(c);
|
||||
}
|
||||
depth += 1;
|
||||
} else if c == ')' {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
result.push(c);
|
||||
} else if c == '\\' {
|
||||
if let Some(next) = chars.next() {
|
||||
result.push(next);
|
||||
}
|
||||
} else if depth > 0 {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
let s = result.trim().to_string();
|
||||
if !s.is_empty() {
|
||||
return Some(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Hex string: <value>
|
||||
if trimmed.starts_with('<') {
|
||||
if let Some(end) = trimmed.find('>') {
|
||||
let hex = &trimmed[1..end];
|
||||
let bytes: Vec<u8> = hex
|
||||
.as_bytes()
|
||||
.chunks(2)
|
||||
.filter_map(|chunk| {
|
||||
if chunk.len() == 2 {
|
||||
u8::from_str_radix(std::str::from_utf8(chunk).ok()?, 16).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if !bytes.is_empty() {
|
||||
return String::from_utf8(bytes).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn minimal_pdf(page_count: u32) -> Vec<u8> {
|
||||
let header = b"%PDF-1.4\n%\x80\x80\x80\x80\n".to_vec();
|
||||
let obj1 = b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n".to_vec();
|
||||
let kids_refs: Vec<String> = (3..3 + page_count).map(|i| format!("{} 0 R", i)).collect();
|
||||
let kids_str = kids_refs.join(" ");
|
||||
let obj2 = format!(
|
||||
"2 0 obj\n<< /Type /Pages /Kids [{}] /Count {} >>\nendobj\n",
|
||||
kids_str, page_count
|
||||
).into_bytes();
|
||||
|
||||
let mut page_objs = Vec::new();
|
||||
for i in 0..page_count {
|
||||
let obj_num = 3 + i;
|
||||
page_objs.push(
|
||||
format!(
|
||||
"{} 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>\nendobj\n",
|
||||
obj_num
|
||||
).into_bytes()
|
||||
);
|
||||
}
|
||||
|
||||
let mut file = header;
|
||||
let mut offsets = Vec::new();
|
||||
|
||||
offsets.push(file.len() as u64);
|
||||
file.extend_from_slice(&obj1);
|
||||
offsets.push(file.len() as u64);
|
||||
file.extend_from_slice(&obj2);
|
||||
for obj in &page_objs {
|
||||
offsets.push(file.len() as u64);
|
||||
file.extend_from_slice(obj);
|
||||
}
|
||||
|
||||
let xref_offset = file.len() as u64;
|
||||
let total_objects = 2 + page_count as usize;
|
||||
file.extend_from_slice(
|
||||
format!("xref\n0 {}\n0000000000 65535 f \n", total_objects + 1).as_bytes()
|
||||
);
|
||||
for &offset in &offsets {
|
||||
file.extend_from_slice(format!("{:010} 00000 n \n", offset).as_bytes());
|
||||
}
|
||||
|
||||
file.extend_from_slice(
|
||||
format!(
|
||||
"trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{}\n%%EOF\n",
|
||||
total_objects + 1,
|
||||
xref_offset
|
||||
).as_bytes()
|
||||
);
|
||||
|
||||
file
|
||||
}
|
||||
|
||||
fn write_temp(data: &[u8], name: &str) -> std::path::PathBuf {
|
||||
let dir = std::env::temp_dir().join("zx_doc_pdf_test");
|
||||
std::fs::create_dir_all(&dir).ok();
|
||||
let path = dir.join(name);
|
||||
std::fs::write(&path, data).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_pdf_metadata_page_count() {
|
||||
let data = minimal_pdf(3);
|
||||
let path = write_temp(&data, "test_3pages.pdf");
|
||||
let meta = read_pdf_metadata(&path).unwrap();
|
||||
assert_eq!(meta.page_count, 3);
|
||||
assert_eq!(meta.file_size, data.len() as u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_pdf_metadata_single_page() {
|
||||
let data = minimal_pdf(1);
|
||||
let path = write_temp(&data, "test_1page.pdf");
|
||||
let meta = read_pdf_metadata(&path).unwrap();
|
||||
assert_eq!(meta.page_count, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_pdf_text_empty() {
|
||||
let data = minimal_pdf(1);
|
||||
let path = write_temp(&data, "test_text_empty.pdf");
|
||||
let pages = extract_pdf_text(&path).unwrap();
|
||||
assert!(pages.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_pdf_with_info_dict() {
|
||||
// Build a PDF with an Info dictionary containing /Title and /Author
|
||||
let info = b"/Title (Test Document) /Author (John Doe)";
|
||||
let pdf = format!(
|
||||
"%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>\nendobj\n4 0 obj\n<< {} >>\nendobj\nxref\n0 5\n0000000000 65535 f \n0000000009 00000 n \n0000000058 00000 n \n0000000115 00000 n \n0000000172 00000 n \ntrailer\n<< /Size 5 /Root 1 0 R /Info 4 0 R >>\nstartxref\n230\n%%EOF\n",
|
||||
std::str::from_utf8(info).unwrap()
|
||||
);
|
||||
let path = write_temp(pdf.as_bytes(), "test_info.pdf");
|
||||
let meta = read_pdf_metadata(&path).unwrap();
|
||||
assert_eq!(meta.page_count, 1);
|
||||
assert_eq!(meta.title, Some("Test Document".into()));
|
||||
assert_eq!(meta.author, Some("John Doe".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fixture_text_pdf() {
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent().unwrap().parent().unwrap();
|
||||
let path = root.join("fixtures/pdf/text_pdf.pdf");
|
||||
if path.exists() {
|
||||
let meta = read_pdf_metadata(&path).unwrap();
|
||||
assert_eq!(meta.page_count, 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fixture_scanned_pdf() {
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent().unwrap().parent().unwrap();
|
||||
let path = root.join("fixtures/pdf/scanned_pdf.pdf");
|
||||
if path.exists() {
|
||||
let meta = read_pdf_metadata(&path).unwrap();
|
||||
assert_eq!(meta.page_count, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,4 +231,83 @@ mod tests {
|
||||
assert_eq!(&back, pos);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_from_camel_case_json() {
|
||||
// Simulate JSON from iOS app / API server: all camelCase field names
|
||||
let json = r#"{"type":"Markdown","blockId":"h1","scrollProgress":0.5}"#;
|
||||
let pos: ReadingPosition = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(
|
||||
pos,
|
||||
ReadingPosition::Markdown { block_id: "h1".into(), scroll_progress: 0.5 }
|
||||
);
|
||||
|
||||
let json = r#"{"type":"Text","lineNumber":5,"scrollProgress":0.3}"#;
|
||||
let pos: ReadingPosition = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(
|
||||
pos,
|
||||
ReadingPosition::Text { line_number: 5, scroll_progress: 0.3 }
|
||||
);
|
||||
|
||||
let json = r#"{"type":"Pdf","pageNumber":3,"pageProgress":0.8,"overallProgress":0.35}"#;
|
||||
let pos: ReadingPosition = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(
|
||||
pos,
|
||||
ReadingPosition::Pdf { page_number: 3, page_progress: 0.8, overall_progress: 0.35 }
|
||||
);
|
||||
|
||||
let json = r#"{"type":"Image","zoomScale":1.5,"offsetX":10.0,"offsetY":20.0}"#;
|
||||
let pos: ReadingPosition = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(
|
||||
pos,
|
||||
ReadingPosition::Image { zoom_scale: 1.5, offset_x: 10.0, offset_y: 20.0 }
|
||||
);
|
||||
|
||||
let json = r#"{"type":"Epub","chapterId":"ch3","chapterProgress":0.6,"overallProgress":0.3}"#;
|
||||
let pos: ReadingPosition = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(
|
||||
pos,
|
||||
ReadingPosition::Epub { chapter_id: "ch3".into(), chapter_progress: 0.6, overall_progress: 0.3 }
|
||||
);
|
||||
|
||||
let json = r#"{"type":"Unknown"}"#;
|
||||
let pos: ReadingPosition = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(pos, ReadingPosition::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_serialize_roundtrip_all_variants() {
|
||||
// Deserialize from camelCase → re-serialize → deserialize again → verify identical
|
||||
let positions = vec![
|
||||
r#"{"type":"Markdown","blockId":"intro","scrollProgress":0.25}"#,
|
||||
r#"{"type":"Text","lineNumber":42,"scrollProgress":0.5}"#,
|
||||
r#"{"type":"Pdf","pageNumber":7,"pageProgress":0.8,"overallProgress":0.35}"#,
|
||||
r#"{"type":"Image","zoomScale":2.0,"offsetX":0.0,"offsetY":100.0}"#,
|
||||
r#"{"type":"Epub","chapterId":"ch1","chapterProgress":0.6,"overallProgress":0.3}"#,
|
||||
r#"{"type":"Unknown"}"#,
|
||||
];
|
||||
for json_str in &positions {
|
||||
let pos1: ReadingPosition = serde_json::from_str(json_str).unwrap();
|
||||
let re_serialized = serde_json::to_string(&pos1).unwrap();
|
||||
let pos2: ReadingPosition = serde_json::from_str(&re_serialized).unwrap();
|
||||
assert_eq!(pos1, pos2, "roundtrip failed for: {json_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_attrs_not_overridden_by_uniffi() {
|
||||
// Verify #[serde(rename)] works correctly alongside #[derive(uniffi::Enum)]:
|
||||
// serde uses renamed camelCase names, not Rust snake_case names.
|
||||
// If uniffi overrode the renames, "blockId" would NOT be recognized.
|
||||
|
||||
// 1. snake_case should FAIL (field is renamed)
|
||||
let json_snake = r#"{"type":"Markdown","block_id":"h1","scroll_progress":0.5}"#;
|
||||
let result: Result<ReadingPosition, _> = serde_json::from_str(json_snake);
|
||||
assert!(result.is_err(), "snake_case should fail: rename=blockId means block_id is unknown");
|
||||
|
||||
// 2. camelCase should SUCCEED
|
||||
let json_camel = r#"{"type":"Markdown","blockId":"h1","scrollProgress":0.5}"#;
|
||||
let result: Result<ReadingPosition, _> = serde_json::from_str(json_camel);
|
||||
assert!(result.is_ok(), "camelCase should succeed: rename=blockId is the expected name");
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,17 +8,54 @@ const SNIPPET_RADIUS: usize = 40;
|
||||
pub struct SearchResult {
|
||||
pub block_id: String,
|
||||
pub line_number: Option<u32>,
|
||||
pub page_number: Option<u32>,
|
||||
pub chapter_id: Option<String>,
|
||||
pub snippet: String,
|
||||
pub match_start: u64,
|
||||
pub match_end: u64,
|
||||
}
|
||||
|
||||
/// Core search: find all occurrences of query_lower in text, call push for each match.
|
||||
/// push receives (snippet, match_start_in_snippet, match_end_in_snippet, match_byte_offset).
|
||||
fn for_each_match_in(
|
||||
text: &str,
|
||||
query_lower: &str,
|
||||
mut push: impl FnMut(String, u64, u64, usize),
|
||||
) {
|
||||
let lower = text.to_lowercase();
|
||||
let mut start = 0;
|
||||
while let Some(pos) = lower[start..].find(query_lower) {
|
||||
let abs_start = start + pos;
|
||||
let abs_end = abs_start + query_lower.len();
|
||||
|
||||
let snippet_start = text.floor_char_boundary(abs_start.saturating_sub(SNIPPET_RADIUS));
|
||||
let snippet_end = text.ceil_char_boundary((abs_end + SNIPPET_RADIUS).min(text.len()));
|
||||
let snippet = if snippet_start > 0 {
|
||||
format!("…{}", &text[snippet_start..snippet_end])
|
||||
} else {
|
||||
text[snippet_start..snippet_end].to_string()
|
||||
};
|
||||
|
||||
push(
|
||||
snippet,
|
||||
(abs_start - snippet_start) as u64,
|
||||
(abs_end - snippet_start) as u64,
|
||||
abs_start,
|
||||
);
|
||||
|
||||
start = abs_end;
|
||||
if start >= lower.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Search within a list of DocumentBlock (Markdown).
|
||||
pub fn search_blocks(blocks: &[DocumentBlock], query: &str) -> Vec<SearchResult> {
|
||||
if query.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let query = query.to_lowercase();
|
||||
let query_lower = query.to_lowercase();
|
||||
let mut results = Vec::new();
|
||||
|
||||
for block in blocks {
|
||||
@ -26,33 +63,18 @@ pub fn search_blocks(blocks: &[DocumentBlock], query: &str) -> Vec<SearchResult>
|
||||
if text.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let lower = text.to_lowercase();
|
||||
let mut start = 0;
|
||||
while let Some(pos) = lower[start..].find(&query) {
|
||||
let abs_start = start + pos;
|
||||
let abs_end = abs_start + query.len();
|
||||
|
||||
let snippet_start = abs_start.saturating_sub(SNIPPET_RADIUS);
|
||||
let snippet_end = (abs_end + SNIPPET_RADIUS).min(text.len());
|
||||
let snippet = if snippet_start > 0 {
|
||||
format!("…{}", &text[snippet_start..snippet_end])
|
||||
} else {
|
||||
text[snippet_start..snippet_end].to_string()
|
||||
};
|
||||
|
||||
let bid = block_id.to_string();
|
||||
for_each_match_in(&text, &query_lower, |snippet, m_start, m_end, _offset| {
|
||||
results.push(SearchResult {
|
||||
block_id: block_id.to_string(),
|
||||
block_id: bid.clone(),
|
||||
line_number: None,
|
||||
page_number: None,
|
||||
chapter_id: None,
|
||||
snippet,
|
||||
match_start: (abs_start - snippet_start) as u64,
|
||||
match_end: (abs_end - snippet_start) as u64,
|
||||
match_start: m_start,
|
||||
match_end: m_end,
|
||||
});
|
||||
|
||||
start = abs_end;
|
||||
if start >= lower.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
results
|
||||
@ -63,38 +85,72 @@ pub fn search_text(content: &str, query: &str) -> Vec<SearchResult> {
|
||||
if query.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let query = query.to_lowercase();
|
||||
let lower = content.to_lowercase();
|
||||
let query_lower = query.to_lowercase();
|
||||
let mut results = Vec::new();
|
||||
let mut start = 0;
|
||||
|
||||
while let Some(pos) = lower[start..].find(&query) {
|
||||
let abs_start = start + pos;
|
||||
let abs_end = abs_start + query.len();
|
||||
|
||||
// Determine line number
|
||||
let line_number = content[..abs_start].lines().count() as u32;
|
||||
|
||||
let snippet_start = abs_start.saturating_sub(SNIPPET_RADIUS);
|
||||
let snippet_end = (abs_end + SNIPPET_RADIUS).min(content.len());
|
||||
let snippet = if snippet_start > 0 {
|
||||
format!("…{}", &content[snippet_start..snippet_end])
|
||||
} else {
|
||||
content[snippet_start..snippet_end].to_string()
|
||||
};
|
||||
|
||||
for_each_match_in(content, &query_lower, |snippet, m_start, m_end, offset| {
|
||||
let line_number = content[..offset].lines().count() as u32;
|
||||
results.push(SearchResult {
|
||||
block_id: format!("line-{line_number}"),
|
||||
line_number: Some(line_number),
|
||||
page_number: None,
|
||||
chapter_id: None,
|
||||
snippet,
|
||||
match_start: (abs_start - snippet_start) as u64,
|
||||
match_end: (abs_end - snippet_start) as u64,
|
||||
match_start: m_start,
|
||||
match_end: m_end,
|
||||
});
|
||||
});
|
||||
|
||||
start = abs_end;
|
||||
if start >= lower.len() {
|
||||
break;
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
/// Search within PDF pages. Each page is a (page_number, text) tuple.
|
||||
pub fn search_pdf_text(pages: &[(u32, &str)], query: &str) -> Vec<SearchResult> {
|
||||
if query.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let query_lower = query.to_lowercase();
|
||||
let mut results = Vec::new();
|
||||
|
||||
for (page_num, page_text) in pages {
|
||||
let pn = *page_num;
|
||||
for_each_match_in(page_text, &query_lower, |snippet, m_start, m_end, _offset| {
|
||||
results.push(SearchResult {
|
||||
block_id: format!("page-{pn}"),
|
||||
line_number: None,
|
||||
page_number: Some(pn),
|
||||
chapter_id: None,
|
||||
snippet,
|
||||
match_start: m_start,
|
||||
match_end: m_end,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
/// Search within EPUB chapters. Each chapter is a (chapter_id, chapter_text) tuple.
|
||||
pub fn search_epub_chapters(chapters: &[(String, &str)], query: &str) -> Vec<SearchResult> {
|
||||
if query.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let query_lower = query.to_lowercase();
|
||||
let mut results = Vec::new();
|
||||
|
||||
for (chapter_id, chapter_text) in chapters {
|
||||
let cid = chapter_id.clone();
|
||||
for_each_match_in(chapter_text, &query_lower, |snippet, m_start, m_end, _offset| {
|
||||
results.push(SearchResult {
|
||||
block_id: cid.clone(),
|
||||
line_number: None,
|
||||
page_number: None,
|
||||
chapter_id: Some(cid.clone()),
|
||||
snippet,
|
||||
match_start: m_start,
|
||||
match_end: m_end,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
results
|
||||
@ -187,4 +243,147 @@ mod tests {
|
||||
let results = search_text(content, "");
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_pdf_text_basic() {
|
||||
let pages = vec![
|
||||
(1u32, "This is page one content."),
|
||||
(2u32, "This is page two with target word."),
|
||||
(3u32, "Page three."),
|
||||
];
|
||||
let results = search_pdf_text(&pages, "target");
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].page_number, Some(2));
|
||||
assert_eq!(results[0].block_id, "page-2");
|
||||
assert!(results[0].chapter_id.is_none());
|
||||
assert!(results[0].line_number.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_pdf_text_multiple_pages() {
|
||||
let pages = vec![
|
||||
(1u32, "Hello world"),
|
||||
(2u32, "Hello again"),
|
||||
];
|
||||
let results = search_pdf_text(&pages, "hello");
|
||||
assert_eq!(results.len(), 2);
|
||||
assert_eq!(results[0].page_number, Some(1));
|
||||
assert_eq!(results[1].page_number, Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_pdf_text_no_match() {
|
||||
let pages = vec![(1u32, "Nothing here.")];
|
||||
let results = search_pdf_text(&pages, "missing");
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_pdf_text_empty_query() {
|
||||
let pages = vec![(1u32, "Content.")];
|
||||
let results = search_pdf_text(&pages, "");
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_epub_chapters_basic() {
|
||||
let chapters = vec![
|
||||
("ch1".to_string(), "Chapter one introduction."),
|
||||
("ch2".to_string(), "Chapter two has the keyword here."),
|
||||
];
|
||||
let results = search_epub_chapters(&chapters, "keyword");
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].chapter_id, Some("ch2".to_string()));
|
||||
assert_eq!(results[0].block_id, "ch2");
|
||||
assert!(results[0].page_number.is_none());
|
||||
assert!(results[0].line_number.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_epub_chapters_multiple() {
|
||||
let chapters = vec![
|
||||
("intro".to_string(), "Welcome to the book."),
|
||||
("ch1".to_string(), "The book begins here."),
|
||||
("ch2".to_string(), "The book continues."),
|
||||
];
|
||||
let results = search_epub_chapters(&chapters, "book");
|
||||
assert_eq!(results.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_epub_chapters_no_match() {
|
||||
let chapters = vec![("ch1".to_string(), "Just text.")];
|
||||
let results = search_epub_chapters(&chapters, "absent");
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_epub_chapters_empty_query() {
|
||||
let chapters = vec![("ch1".to_string(), "Text.")];
|
||||
let results = search_epub_chapters(&chapters, "");
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_result_new_fields_serde() {
|
||||
let result = SearchResult {
|
||||
block_id: "b1".into(),
|
||||
line_number: Some(5),
|
||||
page_number: None,
|
||||
chapter_id: None,
|
||||
snippet: "…snippet…".into(),
|
||||
match_start: 10,
|
||||
match_end: 20,
|
||||
};
|
||||
let json = serde_json::to_string(&result).unwrap();
|
||||
let back: SearchResult = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(back.block_id, "b1");
|
||||
assert_eq!(back.line_number, Some(5));
|
||||
assert_eq!(back.page_number, None);
|
||||
assert_eq!(back.chapter_id, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_result_pdf_fields_serde() {
|
||||
let result = SearchResult {
|
||||
block_id: "page-3".into(),
|
||||
line_number: None,
|
||||
page_number: Some(3),
|
||||
chapter_id: None,
|
||||
snippet: "…pdf match…".into(),
|
||||
match_start: 0,
|
||||
match_end: 5,
|
||||
};
|
||||
let json = serde_json::to_string(&result).unwrap();
|
||||
assert!(json.contains("\"page_number\":3"));
|
||||
let back: SearchResult = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(back.page_number, Some(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_result_epub_fields_serde() {
|
||||
let result = SearchResult {
|
||||
block_id: "ch2".into(),
|
||||
line_number: None,
|
||||
page_number: None,
|
||||
chapter_id: Some("ch2".into()),
|
||||
snippet: "…epub match…".into(),
|
||||
match_start: 0,
|
||||
match_end: 8,
|
||||
};
|
||||
let json = serde_json::to_string(&result).unwrap();
|
||||
assert!(json.contains("\"chapter_id\":\"ch2\""));
|
||||
let back: SearchResult = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(back.chapter_id, Some("ch2".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utf8_multi_byte_boundary_safe() {
|
||||
// The emoji "世界" is 3 bytes each in UTF-8, "🌍" is 4 bytes.
|
||||
// Searching near them should not panic on char boundaries.
|
||||
let content = "Hello 🌍 world! 你好世界 test here.";
|
||||
let results = search_text(content, "test");
|
||||
assert_eq!(results.len(), 1);
|
||||
assert!(results[0].snippet.contains("test"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +167,31 @@ pub fn get_session_v2(session_id: &str) -> Result<ReadingSessionV2, SessionError
|
||||
}
|
||||
}
|
||||
|
||||
/// Clean up stale sessions that have been inactive longer than max_age_ms.
|
||||
/// This should be called on app startup to recover from crash/kill scenarios.
|
||||
/// Removes both Closed and orphaned Active/Paused sessions.
|
||||
pub fn cleanup_stale_sessions_v2(now_ms: i64, max_age_ms: i64) -> u32 {
|
||||
let mut removed = 0u32;
|
||||
|
||||
match (sessions().lock(), trackers().lock()) {
|
||||
(Ok(mut map), Ok(mut tmap)) => {
|
||||
let stale_ids: Vec<String> = map
|
||||
.iter()
|
||||
.filter(|(_, s)| now_ms - s.last_event_at_ms > max_age_ms)
|
||||
.map(|(id, _)| id.clone())
|
||||
.collect();
|
||||
for id in stale_ids {
|
||||
map.remove(&id);
|
||||
tmap.remove(&id);
|
||||
removed += 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
removed
|
||||
}
|
||||
|
||||
/// Remove a closed session from memory. Refuses to remove Active/Paused sessions.
|
||||
pub fn remove_session_v2(session_id: &str) -> Result<(), SessionError> {
|
||||
match (sessions().lock(), trackers().lock()) {
|
||||
@ -309,4 +334,92 @@ mod tests {
|
||||
teardown(&a);
|
||||
teardown(&b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_total_active_seconds_accumulates() {
|
||||
let id = start_reading_session_v2(test_material(), 0).unwrap();
|
||||
// Simulate iOS calling tick() and passing deltas to session events
|
||||
record_session_event_v2(&id, 15_000, 15, None).unwrap();
|
||||
record_session_event_v2(&id, 30_000, 15, None).unwrap();
|
||||
record_session_event_v2(&id, 43_000, 13, None).unwrap();
|
||||
let s = get_session_v2(&id).unwrap();
|
||||
assert_eq!(s.total_active_seconds, 43);
|
||||
teardown(&id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sequence_and_seconds_independent() {
|
||||
let a = start_reading_session_v2(test_material(), 0).unwrap();
|
||||
let b = start_reading_session_v2(ReadingMaterialRef::new("mat_b"), 0).unwrap();
|
||||
record_session_event_v2(&a, 10_000, 10, None).unwrap();
|
||||
record_session_event_v2(&b, 5_000, 5, None).unwrap();
|
||||
record_session_event_v2(&b, 10_000, 5, None).unwrap();
|
||||
assert_eq!(get_session_v2(&a).unwrap().total_active_seconds, 10);
|
||||
assert_eq!(get_session_v2(&a).unwrap().next_sequence, 2);
|
||||
assert_eq!(get_session_v2(&b).unwrap().total_active_seconds, 10);
|
||||
assert_eq!(get_session_v2(&b).unwrap().next_sequence, 3);
|
||||
teardown(&a);
|
||||
teardown(&b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_last_position_updated() {
|
||||
let id = start_reading_session_v2(test_material(), 0).unwrap();
|
||||
let pos1 = ReadingPosition::Markdown { block_id: "intro".into(), scroll_progress: 0.3 };
|
||||
let pos2 = ReadingPosition::Markdown { block_id: "ch1".into(), scroll_progress: 0.8 };
|
||||
record_session_event_v2(&id, 1000, 0, Some(pos1.clone())).unwrap();
|
||||
record_session_event_v2(&id, 2000, 0, Some(pos2.clone())).unwrap();
|
||||
assert_eq!(get_session_v2(&id).unwrap().last_position, Some(pos2));
|
||||
teardown(&id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cleanup_stale_removes_old_sessions() {
|
||||
// Old session: last event at t=1000
|
||||
let old = start_reading_session_v2(test_material(), 0).unwrap();
|
||||
record_session_event_v2(&old, 1000, 0, None).unwrap();
|
||||
|
||||
// Recent session: last event at t=19000 (just 1s ago at now=20000)
|
||||
let recent = start_reading_session_v2(ReadingMaterialRef::new("mat_recent"), 10000).unwrap();
|
||||
record_session_event_v2(&recent, 19000, 0, None).unwrap();
|
||||
|
||||
// At t=20000, clean up sessions inactive for > 5000ms
|
||||
// old: 20000-1000=19000 > 5000 → stale
|
||||
// recent: 20000-19000=1000 < 5000 → NOT stale
|
||||
let removed = cleanup_stale_sessions_v2(20000, 5000);
|
||||
assert_eq!(removed, 1);
|
||||
|
||||
assert!(get_session_v2(&old).is_err());
|
||||
assert!(get_session_v2(&recent).is_ok());
|
||||
|
||||
teardown(&recent);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cleanup_stale_removes_orphaned_active_session() {
|
||||
// Simulate a crash: active session not closed, last event at t=5000
|
||||
let orphaned = start_reading_session_v2(test_material(), 0).unwrap();
|
||||
record_session_event_v2(&orphaned, 5000, 10, None).unwrap();
|
||||
// Session still Active
|
||||
|
||||
// At t=60000, clean up sessions inactive for > 10000ms
|
||||
// 60000 - 5000 = 55000 > 10000 → stale
|
||||
let removed = cleanup_stale_sessions_v2(60000, 10000);
|
||||
assert_eq!(removed, 1);
|
||||
assert!(get_session_v2(&orphaned).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cleanup_stale_zero_when_all_recent() {
|
||||
let active = start_reading_session_v2(test_material(), 0).unwrap();
|
||||
// Last event at t=9500, now at t=10000, max_age=1000
|
||||
// 10000-9500=500 < 1000 → NOT stale
|
||||
record_session_event_v2(&active, 9500, 0, None).unwrap();
|
||||
|
||||
let removed = cleanup_stale_sessions_v2(10000, 1000);
|
||||
assert_eq!(removed, 0);
|
||||
assert!(get_session_v2(&active).is_ok());
|
||||
|
||||
teardown(&active);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,9 @@ pub use zx_document_core::progress::ReadingPosition;
|
||||
pub use zx_document_core::events::ReadingEvent;
|
||||
pub use zx_document_core::reading_material::ReadingMaterialRef;
|
||||
pub use zx_document_core::events_v2::{ReadingEventV2, ReadingEventTypeV2};
|
||||
pub use zx_document_core::epub::{EpubMetadata, EpubChapter};
|
||||
pub use zx_document_core::office::{OfficePreviewConfig, OfficePreviewStrategy};
|
||||
pub use zx_document_core::pdf::{PdfMetadata, PdfPageText};
|
||||
pub use zx_document_core::session_v2::{ReadingSessionV2, ReadingSessionStatus};
|
||||
|
||||
use zx_document_core::blocks as core_blocks;
|
||||
@ -79,6 +82,11 @@ fn export_pending_events_v2(limit: u32, timestamp_ms: i64) -> Vec<ReadingEventV2
|
||||
zx_document_core::events_v2::export_pending_events_v2(limit, timestamp_ms)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn reload_stale_events_v2() -> u32 {
|
||||
zx_document_core::events_v2::reload_stale_events_v2()
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn ack_events_v2(event_ids: Vec<String>) -> u32 {
|
||||
zx_document_core::events_v2::ack_events_v2(&event_ids)
|
||||
@ -301,11 +309,72 @@ fn search_text_content(content: String, query: String) -> Vec<SearchResult> {
|
||||
zx_document_core::search::search_text(&content, &query)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn search_pdf_pages(page_numbers: Vec<u32>, page_texts: Vec<String>, query: String) -> Vec<SearchResult> {
|
||||
let pages: Vec<_> = page_numbers.iter().copied()
|
||||
.zip(page_texts.iter().map(|s| s.as_str()))
|
||||
.collect();
|
||||
zx_document_core::search::search_pdf_text(&pages, &query)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn search_epub_chapters_ffi(chapter_ids: Vec<String>, chapter_texts: Vec<String>, query: String) -> Vec<SearchResult> {
|
||||
let chapters: Vec<_> = chapter_ids.iter().map(|s| s.clone())
|
||||
.zip(chapter_texts.iter().map(|s| s.as_str()))
|
||||
.collect();
|
||||
zx_document_core::search::search_epub_chapters(&chapters, &query)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn create_note_anchor(material_id: String, position: Option<ReadingPosition>) -> NoteAnchor {
|
||||
zx_document_core::anchors::NoteAnchor::from_position(&material_id, position.as_ref())
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn create_note_anchor_from_search(material_id: String, result: SearchResult) -> NoteAnchor {
|
||||
zx_document_core::anchors::NoteAnchor::from_search_result(&material_id, &result)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn restore_position_from_anchor(anchor: NoteAnchor) -> Option<ReadingPosition> {
|
||||
anchor.to_position()
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn read_pdf_metadata_ffi(file_path: String) -> Result<PdfMetadata, DocumentError> {
|
||||
zx_document_core::pdf::read_pdf_metadata(std::path::Path::new(&file_path)).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn extract_pdf_text_ffi(file_path: String) -> Result<Vec<PdfPageText>, DocumentError> {
|
||||
zx_document_core::pdf::extract_pdf_text(std::path::Path::new(&file_path)).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn read_epub_metadata_ffi(file_path: String) -> Result<EpubMetadata, DocumentError> {
|
||||
zx_document_core::epub::read_epub_metadata(std::path::Path::new(&file_path)).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn read_epub_chapters_ffi(file_path: String) -> Result<Vec<EpubChapter>, DocumentError> {
|
||||
zx_document_core::epub::read_epub_chapters(std::path::Path::new(&file_path)).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn get_office_preview_config_ffi(material_type: MaterialType, file_size: u64) -> Result<OfficePreviewConfig, DocumentError> {
|
||||
zx_document_core::office::get_office_preview_config(&material_type, file_size).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn is_office_type_ffi(material_type: MaterialType) -> bool {
|
||||
zx_document_core::office::is_office_type(&material_type)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn cleanup_stale_sessions_ffi(now_ms: i64, max_age_ms: i64) -> u32 {
|
||||
zx_document_core::session_v2::cleanup_stale_sessions_v2(now_ms, max_age_ms)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn push_reading_event(event: ReadingEvent) {
|
||||
zx_document_core::events::push_reading_event(event)
|
||||
@ -500,6 +569,52 @@ pub extern "C" fn ffi_zx_document_ffi_search_text_content_separate(
|
||||
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_pdf_pages_separate(
|
||||
page_numbers_cap: u64, page_numbers_len: u64, page_numbers_data: *const u8,
|
||||
page_texts_cap: u64, page_texts_len: u64, page_texts_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 page_numbers: Vec<u32> = lift_from_raw!(Vec<u32>, page_numbers_cap, page_numbers_len, page_numbers_data);
|
||||
let page_texts: Vec<String> = lift_from_raw!(Vec<String>, page_texts_cap, page_texts_len, page_texts_data);
|
||||
let result: Vec<SearchResult> = crate::search_pdf_pages(page_numbers, page_texts, 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_epub_chapters_ffi_separate(
|
||||
chapter_ids_cap: u64, chapter_ids_len: u64, chapter_ids_data: *const u8,
|
||||
chapter_texts_cap: u64, chapter_texts_len: u64, chapter_texts_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 chapter_ids: Vec<String> = lift_from_raw!(Vec<String>, chapter_ids_cap, chapter_ids_len, chapter_ids_data);
|
||||
let chapter_texts: Vec<String> = lift_from_raw!(Vec<String>, chapter_texts_cap, chapter_texts_len, chapter_texts_data);
|
||||
let result: Vec<SearchResult> = crate::search_epub_chapters_ffi(chapter_ids, chapter_texts, 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_create_note_anchor_from_search_separate(
|
||||
mid_len: i32, mid_data: *const u8,
|
||||
result_cap: u64, result_len: u64, result_data: *const u8,
|
||||
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 search_result: SearchResult = lift_from_raw!(SearchResult, result_cap, result_len, result_data);
|
||||
let anchor: NoteAnchor = crate::create_note_anchor_from_search(material_id, search_result);
|
||||
write_result_to_out!(Ok::<_, DocumentError>(anchor), 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 {
|
||||
@ -529,3 +644,201 @@ fn core_block_from_ffi(block: DocumentBlock) -> core_blocks::DocumentBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ffi_tests {
|
||||
use super::*;
|
||||
use zx_document_core::events_v2;
|
||||
|
||||
fn drain_buffer() {
|
||||
loop {
|
||||
let batch = export_pending_events_v2(1000, 0);
|
||||
if batch.is_empty() { break; }
|
||||
let ids: Vec<String> = batch.iter().map(|e| e.event_id.clone()).collect();
|
||||
ack_events_v2(ids);
|
||||
}
|
||||
}
|
||||
|
||||
// ── V2 Event Pipeline ──
|
||||
|
||||
#[test]
|
||||
fn test_v2_full_event_pipeline() {
|
||||
drain_buffer();
|
||||
events_v2::clear_all_events_v2();
|
||||
|
||||
let mat = ReadingMaterialRef::new("mat_ffi_test".to_string());
|
||||
let sid = start_reading_session_v2(mat, 1000).unwrap();
|
||||
assert!(!sid.is_empty());
|
||||
|
||||
let e1 = push_material_opened_v2(sid.clone(), "mat_ffi_test".to_string(), 1000).unwrap();
|
||||
assert_eq!(e1.event_type, ReadingEventTypeV2::MaterialOpened);
|
||||
assert_eq!(e1.sequence, 1);
|
||||
|
||||
let pos = ReadingPosition::Markdown { block_id: "intro".to_string(), scroll_progress: 0.25 };
|
||||
let e2 = push_position_changed_v2(sid.clone(), "mat_ffi_test".to_string(), pos, 2000).unwrap();
|
||||
assert_eq!(e2.event_type, ReadingEventTypeV2::PositionChanged);
|
||||
assert_eq!(e2.sequence, 2);
|
||||
|
||||
let e3 = push_heartbeat_v2(sid.clone(), "mat_ffi_test".to_string(), 15, None, 5000).unwrap();
|
||||
assert_eq!(e3.event_type, ReadingEventTypeV2::Heartbeat);
|
||||
assert_eq!(e3.active_seconds_delta, 15);
|
||||
|
||||
let e4 = push_marked_as_read_v2(sid.clone(), "mat_ffi_test".to_string(), 10000).unwrap();
|
||||
assert_eq!(e4.event_type, ReadingEventTypeV2::MarkedAsRead);
|
||||
|
||||
push_material_closed_v2(sid.clone(), "mat_ffi_test".to_string(), 0, 12000).unwrap();
|
||||
close_reading_session_v2(sid.clone()).unwrap();
|
||||
|
||||
let exported = export_pending_events_v2(100, 13000);
|
||||
assert!(exported.len() >= 4, "expected >=4, got {}", exported.len());
|
||||
|
||||
let types: Vec<ReadingEventTypeV2> = exported.iter().map(|e| e.event_type.clone()).collect();
|
||||
assert!(types.contains(&ReadingEventTypeV2::MaterialOpened));
|
||||
assert!(types.contains(&ReadingEventTypeV2::PositionChanged));
|
||||
assert!(types.contains(&ReadingEventTypeV2::Heartbeat));
|
||||
assert!(types.contains(&ReadingEventTypeV2::MarkedAsRead));
|
||||
|
||||
let ids: Vec<String> = exported.iter().map(|e| e.event_id.clone()).collect();
|
||||
let acked = ack_events_v2(ids);
|
||||
assert!(acked >= 4);
|
||||
|
||||
let _ = zx_document_core::session_v2::remove_session_v2(&sid);
|
||||
}
|
||||
|
||||
// ── Session Lifecycle ──
|
||||
|
||||
#[test]
|
||||
fn test_session_lifecycle() {
|
||||
let mat = ReadingMaterialRef::new("mat_ffi_life".to_string());
|
||||
let sid = start_reading_session_v2(mat, 0).unwrap();
|
||||
pause_reading_session_v2(sid.clone()).unwrap();
|
||||
resume_reading_session_v2(sid.clone()).unwrap();
|
||||
close_reading_session_v2(sid.clone()).unwrap();
|
||||
let _ = zx_document_core::session_v2::remove_session_v2(&sid);
|
||||
}
|
||||
|
||||
// ── Buffer Recovery ──
|
||||
|
||||
#[test]
|
||||
fn test_mark_failed_and_recover() {
|
||||
drain_buffer();
|
||||
events_v2::clear_all_events_v2();
|
||||
|
||||
let mat = ReadingMaterialRef::new("mat_ffi_recover".to_string());
|
||||
let sid = start_reading_session_v2(mat, 0).unwrap();
|
||||
let e = push_material_opened_v2(sid.clone(), "mat_ffi_recover".to_string(), 1000).unwrap();
|
||||
|
||||
let batch = export_pending_events_v2(100, 2000);
|
||||
assert!(batch.iter().any(|ev| ev.event_id == e.event_id));
|
||||
|
||||
let marked = mark_events_failed_v2(vec![e.event_id.clone()]);
|
||||
assert_eq!(marked, 1);
|
||||
|
||||
let retry = export_pending_events_v2(100, 3000);
|
||||
assert!(retry.iter().any(|ev| ev.event_id == e.event_id));
|
||||
|
||||
ack_events_v2(vec![e.event_id.clone()]);
|
||||
close_reading_session_v2(sid.clone()).unwrap();
|
||||
let _ = zx_document_core::session_v2::remove_session_v2(&sid);
|
||||
}
|
||||
|
||||
// ── Parse → Search → Anchor ──
|
||||
|
||||
#[test]
|
||||
fn test_parse_search_anchor() {
|
||||
let md = "# Hello\n\nParagraph with searchable text.\n\n## Section 2\n\nMore.";
|
||||
let blocks = parse_markdown(md.to_string()).unwrap();
|
||||
assert!(!blocks.is_empty());
|
||||
|
||||
let results = search_markdown_blocks(blocks, "searchable".to_string());
|
||||
assert_eq!(results.len(), 1);
|
||||
assert!(results[0].snippet.to_lowercase().contains("searchable"));
|
||||
|
||||
// Position → Anchor
|
||||
let pos = ReadingPosition::Markdown { block_id: "h1".to_string(), scroll_progress: 0.5 };
|
||||
let anchor = create_note_anchor("mat_ffi".to_string(), Some(pos));
|
||||
match &anchor {
|
||||
NoteAnchor::MarkdownBlock { material_id, block_id, .. } => {
|
||||
assert_eq!(material_id, "mat_ffi");
|
||||
assert_eq!(block_id, "h1");
|
||||
}
|
||||
_ => panic!("expected MarkdownBlock"),
|
||||
}
|
||||
|
||||
// Anchor → Position (roundtrip)
|
||||
let restored = restore_position_from_anchor(anchor);
|
||||
assert!(restored.is_some());
|
||||
|
||||
// SearchResult → Anchor
|
||||
let sr_anchor = create_note_anchor_from_search("mat_ffi".to_string(), results[0].clone());
|
||||
match sr_anchor {
|
||||
NoteAnchor::SearchResultAnchor { material_id, .. } => {
|
||||
assert_eq!(material_id, "mat_ffi");
|
||||
}
|
||||
_ => panic!("expected SearchResultAnchor"),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Text Search ──
|
||||
|
||||
#[test]
|
||||
fn test_text_search() {
|
||||
let results = search_text_content(
|
||||
"Line one\nLine two with keyword\nLine three".to_string(),
|
||||
"keyword".to_string(),
|
||||
);
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].line_number, Some(2));
|
||||
}
|
||||
|
||||
// ── PDF Search ──
|
||||
|
||||
#[test]
|
||||
fn test_pdf_search() {
|
||||
let results = search_pdf_pages(
|
||||
vec![1, 2, 3],
|
||||
vec!["Page 1.".to_string(), "Page 2 with target.".to_string(), "Page 3.".to_string()],
|
||||
"target".to_string(),
|
||||
);
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].page_number, Some(2));
|
||||
}
|
||||
|
||||
// ── EPUB Search ──
|
||||
|
||||
#[test]
|
||||
fn test_epub_search() {
|
||||
let results = search_epub_chapters_ffi(
|
||||
vec!["intro".to_string(), "ch1".to_string()],
|
||||
vec!["Welcome.".to_string(), "Chapter one keyword here.".to_string()],
|
||||
"keyword".to_string(),
|
||||
);
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].chapter_id, Some("ch1".to_string()));
|
||||
}
|
||||
|
||||
// ── V1 Backward Compatibility ──
|
||||
|
||||
#[test]
|
||||
fn test_v1_backward_compat() {
|
||||
let event = ReadingEvent::MaterialOpened {
|
||||
material_id: "mat_v1_ffi".to_string(),
|
||||
timestamp_ms: 1000,
|
||||
};
|
||||
push_reading_event(event);
|
||||
let exported = export_pending_events();
|
||||
assert!(!exported.is_empty());
|
||||
let found = exported.iter().any(|e| {
|
||||
matches!(e, ReadingEvent::MaterialOpened { material_id, .. } if material_id == "mat_v1_ffi")
|
||||
});
|
||||
assert!(found, "V1 event should be exported");
|
||||
clear_exported_events(exported.len() as u32);
|
||||
}
|
||||
|
||||
// ── Error Handling ──
|
||||
|
||||
#[test]
|
||||
fn test_session_not_found() {
|
||||
assert!(close_reading_session_v2("nonexistent".to_string()).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,8 +18,74 @@ namespace zx_document {
|
||||
|
||||
sequence<SearchResult> search_text_content([ByRef] string content, [ByRef] string query);
|
||||
|
||||
sequence<SearchResult> search_pdf_pages(sequence<u32> page_numbers, sequence<string> page_texts, [ByRef] string query);
|
||||
|
||||
sequence<SearchResult> search_epub_chapters_ffi(sequence<string> chapter_ids, sequence<string> chapter_texts, [ByRef] string query);
|
||||
|
||||
NoteAnchor create_note_anchor([ByRef] string material_id, ReadingPosition? position);
|
||||
|
||||
NoteAnchor create_note_anchor_from_search([ByRef] string material_id, SearchResult result);
|
||||
|
||||
ReadingPosition? restore_position_from_anchor(NoteAnchor anchor);
|
||||
|
||||
[Throws=DocumentError]
|
||||
PdfMetadata read_pdf_metadata_ffi([ByRef] string file_path);
|
||||
|
||||
[Throws=DocumentError]
|
||||
sequence<PdfPageText> extract_pdf_text_ffi([ByRef] string file_path);
|
||||
|
||||
[Throws=DocumentError]
|
||||
EpubMetadata read_epub_metadata_ffi([ByRef] string file_path);
|
||||
|
||||
[Throws=DocumentError]
|
||||
sequence<EpubChapter> read_epub_chapters_ffi([ByRef] string file_path);
|
||||
|
||||
[Throws=DocumentError]
|
||||
OfficePreviewConfig get_office_preview_config_ffi(MaterialType material_type, u64 file_size);
|
||||
|
||||
boolean is_office_type_ffi(MaterialType material_type);
|
||||
|
||||
u32 cleanup_stale_sessions_ffi(i64 now_ms, i64 max_age_ms);
|
||||
|
||||
// ── V2 Reading Session ──
|
||||
[Throws=DocumentError]
|
||||
string start_reading_session_v2(ReadingMaterialRef material, i64 timestamp_ms);
|
||||
|
||||
[Throws=DocumentError]
|
||||
void pause_reading_session_v2([ByRef] string session_id);
|
||||
|
||||
[Throws=DocumentError]
|
||||
void resume_reading_session_v2([ByRef] string session_id);
|
||||
|
||||
[Throws=DocumentError]
|
||||
void close_reading_session_v2([ByRef] string session_id);
|
||||
|
||||
// ── V2 Reading Events ──
|
||||
[Throws=DocumentError]
|
||||
ReadingEventV2 push_material_opened_v2([ByRef] string session_id, [ByRef] string material_id, i64 timestamp_ms);
|
||||
|
||||
[Throws=DocumentError]
|
||||
ReadingEventV2 push_material_closed_v2([ByRef] string session_id, [ByRef] string material_id, u32 active_seconds_delta, i64 timestamp_ms);
|
||||
|
||||
[Throws=DocumentError]
|
||||
ReadingEventV2 push_position_changed_v2([ByRef] string session_id, [ByRef] string material_id, ReadingPosition position, i64 timestamp_ms);
|
||||
|
||||
[Throws=DocumentError]
|
||||
ReadingEventV2 push_heartbeat_v2([ByRef] string session_id, [ByRef] string material_id, u32 active_seconds_delta, ReadingPosition? position, i64 timestamp_ms);
|
||||
|
||||
[Throws=DocumentError]
|
||||
ReadingEventV2 push_marked_as_read_v2([ByRef] string session_id, [ByRef] string material_id, i64 timestamp_ms);
|
||||
|
||||
// ── V2 Buffer Management ──
|
||||
sequence<ReadingEventV2> export_pending_events_v2(u32 limit, i64 timestamp_ms);
|
||||
|
||||
u32 ack_events_v2(sequence<string> event_ids);
|
||||
|
||||
u32 mark_events_failed_v2(sequence<string> event_ids);
|
||||
|
||||
u32 reload_stale_events_v2();
|
||||
|
||||
// ── V1 (deprecated) ──
|
||||
void push_reading_event(ReadingEvent event);
|
||||
void update_reading_position([ByRef] string material_id, ReadingPosition position);
|
||||
sequence<ReadingEvent> export_pending_events();
|
||||
@ -35,6 +101,50 @@ enum DocumentError {
|
||||
"IoError",
|
||||
};
|
||||
|
||||
// ── V2 Types ──
|
||||
|
||||
dictionary ReadingMaterialRef {
|
||||
string material_id;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface ReadingSessionStatus {
|
||||
Active();
|
||||
Paused();
|
||||
Closed();
|
||||
};
|
||||
|
||||
dictionary ReadingSessionV2 {
|
||||
string client_session_id;
|
||||
ReadingMaterialRef material;
|
||||
i64 started_at_ms;
|
||||
i64 last_event_at_ms;
|
||||
u64 next_sequence;
|
||||
u32 total_active_seconds;
|
||||
ReadingPosition? last_position;
|
||||
ReadingSessionStatus status;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface ReadingEventTypeV2 {
|
||||
MaterialOpened();
|
||||
MaterialClosed();
|
||||
PositionChanged();
|
||||
Heartbeat();
|
||||
MarkedAsRead();
|
||||
};
|
||||
|
||||
dictionary ReadingEventV2 {
|
||||
string event_id;
|
||||
string client_session_id;
|
||||
string material_id;
|
||||
ReadingEventTypeV2 event_type;
|
||||
ReadingPosition? position;
|
||||
u32 active_seconds_delta;
|
||||
i64 timestamp_ms;
|
||||
u64 sequence;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface MaterialType {
|
||||
Markdown();
|
||||
@ -88,13 +198,14 @@ interface ReadingEvent {
|
||||
|
||||
[Enum]
|
||||
interface NoteAnchor {
|
||||
Material(string material_id);
|
||||
MarkdownBlock(string material_id, string block_id);
|
||||
TextLine(string material_id, u32 line_number);
|
||||
PdfPage(string material_id, u32 page_number);
|
||||
Image(string material_id);
|
||||
EpubChapter(string material_id, string chapter_id);
|
||||
Material(string material_id, ReadingPosition? position_snapshot);
|
||||
MarkdownBlock(string material_id, string block_id, ReadingPosition? position_snapshot);
|
||||
TextLine(string material_id, u32 line_number, ReadingPosition? position_snapshot);
|
||||
PdfPage(string material_id, u32 page_number, ReadingPosition? position_snapshot);
|
||||
Image(string material_id, ReadingPosition? position_snapshot);
|
||||
EpubChapter(string material_id, string chapter_id, ReadingPosition? position_snapshot);
|
||||
KnowledgeItem(string knowledge_item_id);
|
||||
SearchResultAnchor(string material_id, string? block_id, u32? line_number, u32? page_number, string? chapter_id, string snippet);
|
||||
};
|
||||
|
||||
dictionary ImageMeta {
|
||||
@ -107,11 +218,54 @@ dictionary ImageMeta {
|
||||
dictionary SearchResult {
|
||||
string block_id;
|
||||
u32? line_number;
|
||||
u32? page_number;
|
||||
string? chapter_id;
|
||||
string snippet;
|
||||
u64 match_start;
|
||||
u64 match_end;
|
||||
};
|
||||
|
||||
dictionary PdfMetadata {
|
||||
u32 page_count;
|
||||
string? title;
|
||||
string? author;
|
||||
u64 file_size;
|
||||
};
|
||||
|
||||
dictionary EpubMetadata {
|
||||
string? title;
|
||||
string? author;
|
||||
u32 chapter_count;
|
||||
u64 file_size;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface OfficePreviewStrategy {
|
||||
PlatformPreview();
|
||||
ExternalOpen();
|
||||
ServerConvertedPdf();
|
||||
Unsupported();
|
||||
};
|
||||
|
||||
dictionary OfficePreviewConfig {
|
||||
MaterialType material_type;
|
||||
OfficePreviewStrategy strategy;
|
||||
u64 file_size;
|
||||
boolean supports_search_after_conversion;
|
||||
};
|
||||
|
||||
dictionary EpubChapter {
|
||||
string chapter_id;
|
||||
string title;
|
||||
string path;
|
||||
u32 play_order;
|
||||
};
|
||||
|
||||
dictionary PdfPageText {
|
||||
u32 page_number;
|
||||
string text;
|
||||
};
|
||||
|
||||
dictionary TextStats {
|
||||
u32 line_count;
|
||||
u32 word_count;
|
||||
|
||||
128
docs/ffi-troubleshooting.md
Normal file
128
docs/ffi-troubleshooting.md
Normal file
@ -0,0 +1,128 @@
|
||||
# FFI Troubleshooting
|
||||
|
||||
## UniFFI 版本
|
||||
|
||||
本项目使用 UniFFI 0.31,采用 **UDL + proc-macro 混合模式**:
|
||||
- UDL(`zx_document.udl`):定义类型和函数签名
|
||||
- `#[uniffi::export]` proc-macro:生成 C ABI 分发符号
|
||||
- `uniffi-bindgen`:生成 Swift/Kotlin 绑定代码
|
||||
|
||||
## 常见问题
|
||||
|
||||
### `No such module 'ZxDocumentRuntime'`
|
||||
|
||||
**原因**:XCFramework 未正确添加到 Xcode 项目。
|
||||
|
||||
**解决**:
|
||||
1. 确认 `bindings/ios/ZxDocumentRuntime.xcframework` 已生成
|
||||
2. 在 Xcode → Target → General → Frameworks 中添加
|
||||
3. 确保 Embed 设置为 `Do Not Embed`(静态库)
|
||||
|
||||
### `Undefined symbol: _ffi_zx_document_ffi_*`
|
||||
|
||||
**原因**:C ABI 符号未导出。proc-macro 生成的符号名称可能与链接器期望不一致。
|
||||
|
||||
**解决**:
|
||||
1. 确认 `crates/zx_document_ffi/src/lib.rs` 中 `#[no_mangle] pub extern "C"` 函数存在
|
||||
2. 检查 Cargo.toml `crate-type = ["lib", "staticlib", "cdylib"]`
|
||||
3. 运行 `nm target/aarch64-apple-ios/release/libzx_document_ffi.a | grep ffi_` 验证符号
|
||||
4. 确认 UDL 中声明的函数与 `#[uniffi::export]` 函数名称一致
|
||||
|
||||
### `Library not found for -lzx_document_ffi`
|
||||
|
||||
**原因**:Library Search Paths 未设置。
|
||||
|
||||
**解决**:
|
||||
1. Build Settings → Library Search Paths → 添加 `$(PROJECT_DIR)/bindings/ios`
|
||||
2. 或使用绝对路径指向 `.xcframework` 所在目录
|
||||
|
||||
### UniFFI checksum mismatch
|
||||
|
||||
**原因**:UDL 文件更新后未重新生成 Swift binding。
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
uniffi-bindgen generate \
|
||||
--language swift \
|
||||
--out-dir bindings/ios/generated \
|
||||
crates/zx_document_ffi/src/zx_document.udl
|
||||
```
|
||||
|
||||
### `setup_scaffolding!()` not called
|
||||
|
||||
**原因**:`uniffi::setup_scaffolding!()` 必须在 lib.rs 的第一行调用。
|
||||
|
||||
**解决**:确认 `crates/zx_document_core/src/lib.rs` 第一行是 `uniffi::setup_scaffolding!();`
|
||||
|
||||
### 新增类型/函数后 Swift 不可见
|
||||
|
||||
**原因**:只在 Rust 侧添加了类型/函数,但未更新:
|
||||
1. UDL 文件声明
|
||||
2. FFI crate `pub use` 重导出
|
||||
3. Swift binding 重新生成
|
||||
|
||||
**完整的 FFI 接入步骤**:
|
||||
```bash
|
||||
# 1. Rust 侧添加类型 + 函数 + UDL 声明
|
||||
# 2. FFI crate: pub use 重导出 + #[uniffi::export] 包装
|
||||
# 3. 重新生成 Swift
|
||||
uniffi-bindgen generate --language swift \
|
||||
--out-dir bindings/ios/generated \
|
||||
crates/zx_document_ffi/src/zx_document.udl
|
||||
# 4. 验证编译
|
||||
cargo build --release --target aarch64-apple-ios -p zx_document_ffi
|
||||
```
|
||||
|
||||
### `cargo build` 目标平台不匹配
|
||||
|
||||
**原因**:macOS 上构建 iOS 目标需要 Apple Silicon target。
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
rustup target add aarch64-apple-ios aarch64-apple-ios-sim
|
||||
```
|
||||
|
||||
### `infer` / `comrak` / `zip` crate 编译失败
|
||||
|
||||
**原因**:某些 crate 在 iOS target 下有 C 依赖。
|
||||
|
||||
**解决**:
|
||||
- `comrak`:需要启用 `--features` 中的 `onig` 或使用 `syntect` 后端
|
||||
- `zip`:纯 Rust,无特殊要求
|
||||
- 确保 `CC` 环境变量指向 Xcode toolchain
|
||||
|
||||
### XCFramework 构建注意事项
|
||||
|
||||
1. 必须分别编译 device 和 simulator 目标
|
||||
2. `lipo` 不需要(两个 .a 文件各自是 fat binary)
|
||||
3. header 文件由 `uniffi-bindgen` 生成:`bindings/ios/generated/zx_documentFFI.h`
|
||||
4. modulemap 文件创建:
|
||||
```
|
||||
framework module ZxDocumentRuntime {
|
||||
header "zx_documentFFI.h"
|
||||
export *
|
||||
}
|
||||
```
|
||||
|
||||
### UDL 类型不支持
|
||||
|
||||
**问题类型**:
|
||||
- `Vec<(String, &str)>` — 不支持 tuple
|
||||
- 泛型函数 — 不支持
|
||||
- 生命周期参数 — 不支持
|
||||
|
||||
**解决**:在 FFI 层创建包装类型。例如 `search_pdf_text(&[(u32, &str)])` → FFI 包装为 `search_pdf_pages(Vec<u32>, Vec<String>)`。
|
||||
|
||||
### Rust 与 iOS 持有引用
|
||||
|
||||
UniFFI 生成的对象是 **值类型**(序列化跨 FFI 边界传递),不是引用类型。Rust 侧的 `Mutex` 保护全局状态,iOS 侧通过函数调用读写状态。
|
||||
|
||||
### 验证清单
|
||||
|
||||
- [ ] `cargo build --release --target aarch64-apple-ios` 通过
|
||||
- [ ] `cargo build --release --target aarch64-apple-ios-sim` 通过
|
||||
- [ ] `nm` 验证 C ABI 符号存在
|
||||
- [ ] Swift binding 生成并编译通过
|
||||
- [ ] XCFramework 包含 device + simulator slices
|
||||
- [ ] header + modulemap 正确
|
||||
- [ ] demo App 链接成功
|
||||
@ -115,24 +115,180 @@ do {
|
||||
}
|
||||
```
|
||||
|
||||
### 阅读事件集成
|
||||
### V2 阅读事件集成(推荐)
|
||||
|
||||
V2 API 提供 session 管理、事件缓冲、ack 确认、crash recovery 等完整能力。
|
||||
|
||||
#### 创建阅读目标和会话
|
||||
|
||||
```swift
|
||||
// Rust 生成事件 → Swift 缓存 + 上传
|
||||
class ReadingEventCollector {
|
||||
private var buffer: [ReadingEvent] = []
|
||||
// 1. 创建 ReadingMaterialRef(Rust 不存储 readingTargetType,iOS 上传时补充)
|
||||
let material = ReadingMaterialRef(materialId: "mat_001")
|
||||
|
||||
func collect() {
|
||||
// Rust 侧事件暂存
|
||||
}
|
||||
// 2. 启动阅读会话(返回 clientSessionId)
|
||||
let timestamp = Int64(Date().timeIntervalSince1970 * 1000)
|
||||
let sessionId = try startReadingSessionV2(material: material, timestampMs: timestamp)
|
||||
```
|
||||
|
||||
func flush() {
|
||||
// POST buffer → /reading/events
|
||||
// clear buffer
|
||||
#### 推送 5 种事件
|
||||
|
||||
```swift
|
||||
// MaterialOpened — 打开资料
|
||||
let opened = try pushMaterialOpenedV2(
|
||||
sessionId: sessionId,
|
||||
materialId: "mat_001",
|
||||
timestampMs: now()
|
||||
)
|
||||
|
||||
// PositionChanged — 位置变化
|
||||
let pos = ReadingPosition.markdown(blockId: "intro", scrollProgress: 0.25)
|
||||
let changed = try pushPositionChangedV2(
|
||||
sessionId: sessionId,
|
||||
materialId: "mat_001",
|
||||
position: pos,
|
||||
timestampMs: now()
|
||||
)
|
||||
|
||||
// Heartbeat — 心跳(含 activeSecondsDelta)
|
||||
let hb = try pushHeartbeatV2(
|
||||
sessionId: sessionId,
|
||||
materialId: "mat_001",
|
||||
activeSecondsDelta: 15,
|
||||
position: nil,
|
||||
timestampMs: now()
|
||||
)
|
||||
|
||||
// MarkedAsRead — 标记已读
|
||||
let mar = try pushMarkedAsReadV2(
|
||||
sessionId: sessionId,
|
||||
materialId: "mat_001",
|
||||
timestampMs: now()
|
||||
)
|
||||
|
||||
// MaterialClosed — 关闭资料
|
||||
let closed = try pushMaterialClosedV2(
|
||||
sessionId: sessionId,
|
||||
materialId: "mat_001",
|
||||
activeSecondsDelta: 0,
|
||||
timestampMs: now()
|
||||
)
|
||||
```
|
||||
|
||||
#### Export + Ack 流程
|
||||
|
||||
```swift
|
||||
// 1. 导出待上传事件(标记为 Exported)
|
||||
let events = exportPendingEventsV2(limit: 100, timestampMs: now())
|
||||
|
||||
// 2. 构造上传请求(iOS 补充 readingTargetType/platform/appVersion/timezone)
|
||||
let uploadItems = events.map { event in
|
||||
ReadingEventUploadItem(
|
||||
eventId: event.eventId,
|
||||
clientSessionId: event.clientSessionId,
|
||||
materialId: event.materialId,
|
||||
eventType: event.eventType,
|
||||
position: event.position,
|
||||
activeSecondsDelta: event.activeSecondsDelta,
|
||||
clientTimestampMs: event.timestampMs,
|
||||
sequence: event.sequence,
|
||||
// iOS 补充字段
|
||||
readingTargetType: getTargetType(for: event.materialId),
|
||||
platform: "ios",
|
||||
appVersion: Bundle.main.appVersion,
|
||||
clientTimezoneOffsetMinutes: Int(TimeZone.current.secondsFromGMT() / 60)
|
||||
)
|
||||
}
|
||||
|
||||
// 3. 上传到 API
|
||||
api.uploadEvents(uploadItems) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
// 成功后 ACK(从 buffer 移除)
|
||||
let ids = events.map(\.eventId)
|
||||
ackEventsV2(eventIds: ids)
|
||||
case .failure:
|
||||
// 失败标记为 Failed(下次 export 重试)
|
||||
let ids = events.map(\.eventId)
|
||||
markEventsFailedV2(eventIds: ids)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### App 启动恢复
|
||||
|
||||
```swift
|
||||
// App 启动时调用,恢复上次未 ack 的事件
|
||||
func applicationDidFinishLaunching() {
|
||||
reloadStaleEventsV2() // 内部 Pending/Exported→Pending,可重新 export
|
||||
}
|
||||
```
|
||||
|
||||
#### 搜索和笔记锚点
|
||||
|
||||
```swift
|
||||
// 1. 解析 Markdown
|
||||
let blocks = try parseMarkdown(content: mdContent)
|
||||
|
||||
// 2. 搜索
|
||||
let results = searchMarkdownBlocks(blocks: blocks, query: "keyword")
|
||||
|
||||
// 3. 从搜索结果创建笔记锚点
|
||||
if let firstResult = results.first {
|
||||
let anchor = createNoteAnchorFromSearch(
|
||||
materialId: "mat_001",
|
||||
result: firstResult
|
||||
)
|
||||
// anchor 包含 materialId/blockId/pageNumber/chapterId/snippet
|
||||
}
|
||||
|
||||
// 4. 从阅读位置创建锚点
|
||||
let pos = ReadingPosition.pdf(pageNumber: 3, pageProgress: 0.5, overallProgress: 0.1)
|
||||
let anchor = createNoteAnchor(materialId: "mat_001", position: pos)
|
||||
// → NoteAnchor.pdfPage(materialId: "mat_001", pageNumber: 3, positionSnapshot: pos)
|
||||
|
||||
// 5. 从锚点恢复阅读位置
|
||||
if let restored = restorePositionFromAnchor(anchor: anchor) {
|
||||
// navigate to restored position
|
||||
}
|
||||
|
||||
// PDF 搜索
|
||||
let pages = searchPdfPages(
|
||||
pageNumbers: [1, 2, 3],
|
||||
pageTexts: ["page 1 text", "page 2 text", "page 3 text"],
|
||||
query: "keyword"
|
||||
) // → [SearchResult] with page_number
|
||||
|
||||
// EPUB 搜索
|
||||
let chapters = searchEpubChaptersFfi(
|
||||
chapterIds: ["intro", "ch1"],
|
||||
chapterTexts: ["Welcome", "Content with keyword"],
|
||||
query: "keyword"
|
||||
) // → [SearchResult] with chapter_id
|
||||
```
|
||||
|
||||
#### PDF/EPUB 元数据
|
||||
|
||||
```swift
|
||||
// PDF
|
||||
let pdfMeta = try readPdfMetadataFfi(filePath: "/path/to/doc.pdf")
|
||||
print("Pages: \(pdfMeta.pageCount), Title: \(pdfMeta.title ?? "N/A")")
|
||||
|
||||
// EPUB
|
||||
let epubMeta = try readEpubMetadataFfi(filePath: "/path/to/book.epub")
|
||||
print("Chapters: \(epubMeta.chapterCount)")
|
||||
let chapters = try readEpubChaptersFfi(filePath: "/path/to/book.epub")
|
||||
for ch in chapters {
|
||||
print(" \(ch.title) — \(ch.path)")
|
||||
}
|
||||
|
||||
// Office
|
||||
let config = try getOfficePreviewConfigFfi(
|
||||
materialType: .word,
|
||||
fileSize: 1024
|
||||
)
|
||||
print("Strategy: \(config.strategy)") // PlatformPreview
|
||||
```
|
||||
|
||||
## 6. 使用真实 XCFramework 的项目示例
|
||||
|
||||
见 `bindings/ios/demo/` 目录,包含最小 SwiftUI App 示例:
|
||||
|
||||
160
docs/reading-event-protocol.md
Normal file
160
docs/reading-event-protocol.md
Normal file
@ -0,0 +1,160 @@
|
||||
# ReadingEvent V2 协议
|
||||
|
||||
## 概述
|
||||
|
||||
V2 阅读事件协议定义了一套完整的学习行为采集框架:**阅读会话 → 事件生成 → buffer 暂存 → export 导出 → ack 确认**。
|
||||
|
||||
与 V1 的区别:
|
||||
- V1:无 session,无 ack,无 id 追踪
|
||||
- V2:session 管理 + 全局事件 buffer + export/ack 确认 + crash recovery
|
||||
|
||||
## 核心概念
|
||||
|
||||
### ReadingSessionV2
|
||||
|
||||
一次阅读会话。iOS App 一次打开资料即创建一个 session。
|
||||
|
||||
```rust
|
||||
pub struct ReadingSessionV2 {
|
||||
pub client_session_id: String, // UUID v4,会话标识
|
||||
pub material: ReadingMaterialRef, // 被阅读的资料
|
||||
pub started_at_ms: i64, // 会话开始时间
|
||||
pub last_event_at_ms: i64, // 最后一次事件时间
|
||||
pub next_sequence: u64, // 下一个事件序号(从 1 开始)
|
||||
pub total_active_seconds: u32, // 累计活跃阅读秒数
|
||||
pub last_position: Option<ReadingPosition>, // 最后位置
|
||||
pub status: ReadingSessionStatus, // Active/Paused/Closed
|
||||
}
|
||||
```
|
||||
|
||||
#### 生命周期
|
||||
|
||||
```
|
||||
start → Active → (pause → Paused → resume → Active)* → close → Closed
|
||||
```
|
||||
|
||||
- **Active**:可推送任意类型事件
|
||||
- **Paused**:仅可推送 delta=0 事件(PositionChanged/MarkedAsRead)
|
||||
- **Closed**:不可推送事件,不可重新打开
|
||||
|
||||
### ReadingMaterialRef
|
||||
|
||||
```rust
|
||||
pub struct ReadingMaterialRef {
|
||||
pub material_id: String,
|
||||
}
|
||||
```
|
||||
|
||||
Rust 不存储 `readingTargetType`(如 knowledge/material/course/note),由 iOS 上传时补充。
|
||||
|
||||
### ReadingEventV2
|
||||
|
||||
```rust
|
||||
pub struct ReadingEventV2 {
|
||||
pub event_id: String, // UUID v4,全局唯一事件 ID
|
||||
pub client_session_id: String, // 关联的会话 ID
|
||||
pub material_id: String, // 资料 ID
|
||||
pub event_type: ReadingEventTypeV2, // 事件类型
|
||||
pub position: Option<ReadingPosition>, // 阅读位置(camelCase JSON)
|
||||
pub active_seconds_delta: u32, // 距上次事件的活跃秒数
|
||||
pub timestamp_ms: i64, // 客户端时间戳
|
||||
pub sequence: u64, // session 内递增序号(1-based)
|
||||
}
|
||||
```
|
||||
|
||||
#### eventType 取值
|
||||
|
||||
| Rust | API JSON |
|
||||
|------|----------|
|
||||
| `MaterialOpened` | `material_opened` |
|
||||
| `MaterialClosed` | `material_closed` |
|
||||
| `PositionChanged` | `position_changed` |
|
||||
| `Heartbeat` | `heartbeat` |
|
||||
| `MarkedAsRead` | `marked_as_read` |
|
||||
|
||||
#### activeSecondsDelta 规则
|
||||
|
||||
| 事件 | delta |
|
||||
|------|-------|
|
||||
| MaterialOpened | 0 |
|
||||
| PositionChanged | 0 |
|
||||
| MarkedAsRead | 0 |
|
||||
| Heartbeat | ActiveTimeTracker.tick() 返回值 |
|
||||
| MaterialClosed | ActiveTimeTracker.close() 返回值 |
|
||||
|
||||
### ActiveTimeTracker
|
||||
|
||||
iOS 控制 tick 节奏,Rust 计算 delta:
|
||||
|
||||
```
|
||||
start(ts) → tick(ts+15s) → tick(ts+30s) → close(ts+43s)
|
||||
delta=15 delta=15 delta=13
|
||||
```
|
||||
|
||||
- 暂停时不累计时间
|
||||
- 时间倒退返回 0
|
||||
- 余数毫秒累积到下次 tick
|
||||
|
||||
### EventBuffer 状态机
|
||||
|
||||
```
|
||||
push
|
||||
│
|
||||
▼
|
||||
┌─ Pending ──┐
|
||||
│ │
|
||||
export() reload_stale()
|
||||
│ │
|
||||
▼ │
|
||||
Exported ───────┘
|
||||
│
|
||||
┌─────┴─────┐
|
||||
│ │
|
||||
ack() mark_failed()
|
||||
│ │
|
||||
▼ ▼
|
||||
(removed) Failed
|
||||
│
|
||||
export() ──→ 重试
|
||||
```
|
||||
|
||||
- **Pending**:新事件,等待导出
|
||||
- **Exported**:已导出,等待 ack。crash 后 `reload_stale_events()` 恢复为 Pending
|
||||
- **Failed**:上传失败,下次 export 重试
|
||||
- **Ack 后删除**:从 buffer 移除
|
||||
|
||||
### 溢出驱逐
|
||||
|
||||
Buffer 容量:1000 条。满时驱逐顺序:
|
||||
1. Failed(最早失败的)
|
||||
2. Exported(最早导出的)
|
||||
3. 最早 Pending
|
||||
|
||||
## API 上传字段映射
|
||||
|
||||
| Rust ReadingEventV2 | API ReadingEventUploadItem | 来源 |
|
||||
|---------------------|---------------------------|------|
|
||||
| event_id | eventId | Rust UUID |
|
||||
| client_session_id | clientSessionId | Rust UUID |
|
||||
| material_id | materialId | Rust 保存 |
|
||||
| event_type | eventType | Rust→snake_case |
|
||||
| position | position | Rust(camelCase JSON,clamped) |
|
||||
| active_seconds_delta | activeSecondsDelta | ActiveTimeTracker |
|
||||
| timestamp_ms | clientTimestampMs | Rust |
|
||||
| sequence | sequence | session 内递增 |
|
||||
| — | readingTargetType | **iOS 补充** |
|
||||
| — | platform | **iOS 补充** |
|
||||
| — | appVersion | **iOS 补充** |
|
||||
| — | clientTimezoneOffsetMinutes | **iOS 补充** |
|
||||
|
||||
## iOS 上传流程
|
||||
|
||||
```
|
||||
1. Rust export_pending_events_v2(limit, timestamp) → Vec<ReadingEventV2>
|
||||
2. iOS 遍历事件,补充 readingTargetType/platform/appVersion/timezone
|
||||
3. iOS 构造 API 请求体
|
||||
4. POST /reading/events
|
||||
5. 成功 → Rust ack_events_v2(eventIds)
|
||||
6. 失败 → Rust mark_events_failed_v2(eventIds)
|
||||
7. App 启动 → Rust reload_stale_events_v2()
|
||||
```
|
||||
@ -28,12 +28,45 @@
|
||||
| Excel | `.xls` `.xlsx` | PlatformPreview | 不解析 | QuickLook / 系统预览 |
|
||||
| PPT | `.ppt` `.pptx` | ExternalOpen | 不解析 | 外部 App 打开 |
|
||||
|
||||
### 后续支持
|
||||
### 第二版(已支持)
|
||||
|
||||
| 格式 | 扩展名 | PreviewMode | Rust 职责 | App 职责 |
|
||||
|------|--------|-------------|----------|---------|
|
||||
| EPUB | `.epub` | NativeReader | 解析 OPF/spine/NCX TOC,读取章节列表和元数据 | WebView 渲染章节 HTML |
|
||||
| PDF | `.pdf` | PlatformPreview | 读取元数据(页数/标题/作者),通过 pdfium feature 支持文本提取和搜索 | 系统预览(iOS PDFKit)+ 搜索接口 |
|
||||
|
||||
#### EPUB 能力
|
||||
|
||||
| 特性 | 状态 |
|
||||
|------|------|
|
||||
| 元数据(title/author) | ✅ read_epub_metadata |
|
||||
| 章节列表(spine + NCX TOC) | ✅ read_epub_chapters |
|
||||
| EPUB2 NCX 解析 | ✅ |
|
||||
| EPUB3 NAV 解析 | ⚠️ 降级为 "Chapter N"(#100) |
|
||||
|
||||
#### PDF 能力
|
||||
|
||||
| 特性 | 状态 |
|
||||
|------|------|
|
||||
| 元数据(page_count/title/author) | ✅ read_pdf_metadata |
|
||||
| 页数检测(/Type /Page + /Count 回退) | ✅ |
|
||||
| 文本提取 | ⚠️ stub,需 pdfium feature |
|
||||
| 搜索 | ✅ search_pdf_text(需预提取文本) |
|
||||
|
||||
#### Office 能力
|
||||
|
||||
| 类型 | PreviewMode | Strategy | 搜索 |
|
||||
|------|-------------|----------|------|
|
||||
| Word (.doc/.docx) | PlatformPreview | QLPreviewController | 否(可 Server 转 PDF 后搜索)|
|
||||
| Excel (.xls/.xlsx) | PlatformPreview | QLPreviewController | 否 |
|
||||
| PowerPoint (.ppt/.pptx) | ExternalOpen | 外部 App 打开 | 否 |
|
||||
|
||||
### 第三版(计划)
|
||||
|
||||
| 格式 | 目标 PreviewMode | 备注 |
|
||||
|------|-----------------|------|
|
||||
| EPUB | NativeReader | Rust 解析 OPF/spine/nav,App WebView 渲染章节 |
|
||||
| PDF | NativeReader(增强) | 评估 PDFium,支持文本提取和搜索 |
|
||||
| PDF 增强 | NativeReader(增强) | pdfium 文本提取 + 页级搜索 + 阅读位置同步 |
|
||||
| EPUB3 NAV | NativeReader | 解析 `<nav epub:type="toc">` 获取真实章节标题 |
|
||||
|
||||
### 明确不做
|
||||
|
||||
|
||||
BIN
fixtures/epub/epub_with_toc.epub
Normal file
BIN
fixtures/epub/epub_with_toc.epub
Normal file
Binary file not shown.
BIN
fixtures/epub/simple.epub
Normal file
BIN
fixtures/epub/simple.epub
Normal file
Binary file not shown.
BIN
fixtures/invalid_file.bin
Normal file
BIN
fixtures/invalid_file.bin
Normal file
Binary file not shown.
45
fixtures/markdown/complex_markdown.md
Normal file
45
fixtures/markdown/complex_markdown.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Document Title (h1)
|
||||
|
||||
Introduction paragraph with **bold** and *italic* text. This is the first paragraph of the document.
|
||||
|
||||
## Section One (h2)
|
||||
|
||||
This section contains a code block with language info.
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
### Subsection (h3)
|
||||
|
||||
- Unordered item 1
|
||||
- Unordered item 2
|
||||
- Unordered item 3
|
||||
|
||||
1. Ordered item 1
|
||||
2. Ordered item 2
|
||||
3. Ordered item 3
|
||||
|
||||
## Section Two — Tables & Quotes
|
||||
|
||||
| Name | Age | City |
|
||||
|------|-----|------|
|
||||
| Alice | 30 | NYC |
|
||||
| Bob | 25 | LA |
|
||||
| Carol | 28 | SF |
|
||||
|
||||
> This is a blockquote with some wisdom: "The only way to do great work is to love what you do."
|
||||
|
||||
## Section Three — Images & Rules
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
### Final Section
|
||||
|
||||
Final paragraph with `inline code` and a [link](https://example.com).
|
||||
|
||||
---
|
||||
1054
fixtures/markdown/large_markdown.md
Normal file
1054
fixtures/markdown/large_markdown.md
Normal file
File diff suppressed because it is too large
Load Diff
22
fixtures/pdf/scanned_pdf.pdf
Normal file
22
fixtures/pdf/scanned_pdf.pdf
Normal file
@ -0,0 +1,22 @@
|
||||
%PDF-1.4
|
||||
%€€€€
|
||||
1 0 obj
|
||||
<< /Type /Catalog /Pages 2 0 R >>
|
||||
endobj
|
||||
2 0 obj
|
||||
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
|
||||
endobj
|
||||
3 0 obj
|
||||
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
|
||||
endobj
|
||||
xref
|
||||
0 4
|
||||
0000000000 65535 f
|
||||
0000000015 00000 n
|
||||
0000000064 00000 n
|
||||
0000000121 00000 n
|
||||
trailer
|
||||
<< /Size 4 /Root 1 0 R >>
|
||||
startxref
|
||||
192
|
||||
%%EOF
|
||||
26
fixtures/pdf/text_pdf.pdf
Normal file
26
fixtures/pdf/text_pdf.pdf
Normal file
@ -0,0 +1,26 @@
|
||||
%PDF-1.4
|
||||
%€€€€
|
||||
1 0 obj
|
||||
<< /Type /Catalog /Pages 2 0 R >>
|
||||
endobj
|
||||
2 0 obj
|
||||
<< /Type /Pages /Kids [3 0 R 4 0 R] /Count 2 >>
|
||||
endobj
|
||||
3 0 obj
|
||||
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
|
||||
endobj
|
||||
4 0 obj
|
||||
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
|
||||
endobj
|
||||
xref
|
||||
0 5
|
||||
0000000000 65535 f
|
||||
0000000015 00000 n
|
||||
0000000064 00000 n
|
||||
0000000127 00000 n
|
||||
0000000198 00000 n
|
||||
trailer
|
||||
<< /Size 5 /Root 1 0 R >>
|
||||
startxref
|
||||
269
|
||||
%%EOF
|
||||
500
fixtures/text/large_text.txt
Normal file
500
fixtures/text/large_text.txt
Normal file
@ -0,0 +1,500 @@
|
||||
Line 1: This is a long text line number 1 with searchable content spread across the file.
|
||||
Line 2: This is a long text line number 2 with searchable content spread across the file.
|
||||
Line 3: This is a long text line number 3 with searchable content spread across the file.
|
||||
Line 4: This is a long text line number 4 with searchable content spread across the file.
|
||||
Line 5: This is a long text line number 5 with searchable content spread across the file.
|
||||
Line 6: This is a long text line number 6 with searchable content spread across the file.
|
||||
Line 7: This is a long text line number 7 with searchable content spread across the file.
|
||||
Line 8: This is a long text line number 8 with searchable content spread across the file.
|
||||
Line 9: This is a long text line number 9 with searchable content spread across the file.
|
||||
Line 10: This is a long text line number 10 with searchable content spread across the file.
|
||||
Line 11: This is a long text line number 11 with searchable content spread across the file.
|
||||
Line 12: This is a long text line number 12 with searchable content spread across the file.
|
||||
Line 13: This is a long text line number 13 with searchable content spread across the file.
|
||||
Line 14: This is a long text line number 14 with searchable content spread across the file.
|
||||
Line 15: This is a long text line number 15 with searchable content spread across the file.
|
||||
Line 16: This is a long text line number 16 with searchable content spread across the file.
|
||||
Line 17: This is a long text line number 17 with searchable content spread across the file.
|
||||
Line 18: This is a long text line number 18 with searchable content spread across the file.
|
||||
Line 19: This is a long text line number 19 with searchable content spread across the file.
|
||||
Line 20: This is a long text line number 20 with searchable content spread across the file.
|
||||
Line 21: This is a long text line number 21 with searchable content spread across the file.
|
||||
Line 22: This is a long text line number 22 with searchable content spread across the file.
|
||||
Line 23: This is a long text line number 23 with searchable content spread across the file.
|
||||
Line 24: This is a long text line number 24 with searchable content spread across the file.
|
||||
Line 25: This is a long text line number 25 with searchable content spread across the file.
|
||||
Line 26: This is a long text line number 26 with searchable content spread across the file.
|
||||
Line 27: This is a long text line number 27 with searchable content spread across the file.
|
||||
Line 28: This is a long text line number 28 with searchable content spread across the file.
|
||||
Line 29: This is a long text line number 29 with searchable content spread across the file.
|
||||
Line 30: This is a long text line number 30 with searchable content spread across the file.
|
||||
Line 31: This is a long text line number 31 with searchable content spread across the file.
|
||||
Line 32: This is a long text line number 32 with searchable content spread across the file.
|
||||
Line 33: This is a long text line number 33 with searchable content spread across the file.
|
||||
Line 34: This is a long text line number 34 with searchable content spread across the file.
|
||||
Line 35: This is a long text line number 35 with searchable content spread across the file.
|
||||
Line 36: This is a long text line number 36 with searchable content spread across the file.
|
||||
Line 37: This is a long text line number 37 with searchable content spread across the file.
|
||||
Line 38: This is a long text line number 38 with searchable content spread across the file.
|
||||
Line 39: This is a long text line number 39 with searchable content spread across the file.
|
||||
Line 40: This is a long text line number 40 with searchable content spread across the file.
|
||||
Line 41: This is a long text line number 41 with searchable content spread across the file.
|
||||
Line 42: This is a long text line number 42 with searchable content spread across the file.
|
||||
Line 43: This is a long text line number 43 with searchable content spread across the file.
|
||||
Line 44: This is a long text line number 44 with searchable content spread across the file.
|
||||
Line 45: This is a long text line number 45 with searchable content spread across the file.
|
||||
Line 46: This is a long text line number 46 with searchable content spread across the file.
|
||||
Line 47: This is a long text line number 47 with searchable content spread across the file.
|
||||
Line 48: This is a long text line number 48 with searchable content spread across the file.
|
||||
Line 49: This is a long text line number 49 with searchable content spread across the file.
|
||||
Line 50: This is a long text line number 50 with searchable content spread across the file.
|
||||
Line 51: This is a long text line number 51 with searchable content spread across the file.
|
||||
Line 52: This is a long text line number 52 with searchable content spread across the file.
|
||||
Line 53: This is a long text line number 53 with searchable content spread across the file.
|
||||
Line 54: This is a long text line number 54 with searchable content spread across the file.
|
||||
Line 55: This is a long text line number 55 with searchable content spread across the file.
|
||||
Line 56: This is a long text line number 56 with searchable content spread across the file.
|
||||
Line 57: This is a long text line number 57 with searchable content spread across the file.
|
||||
Line 58: This is a long text line number 58 with searchable content spread across the file.
|
||||
Line 59: This is a long text line number 59 with searchable content spread across the file.
|
||||
Line 60: This is a long text line number 60 with searchable content spread across the file.
|
||||
Line 61: This is a long text line number 61 with searchable content spread across the file.
|
||||
Line 62: This is a long text line number 62 with searchable content spread across the file.
|
||||
Line 63: This is a long text line number 63 with searchable content spread across the file.
|
||||
Line 64: This is a long text line number 64 with searchable content spread across the file.
|
||||
Line 65: This is a long text line number 65 with searchable content spread across the file.
|
||||
Line 66: This is a long text line number 66 with searchable content spread across the file.
|
||||
Line 67: This is a long text line number 67 with searchable content spread across the file.
|
||||
Line 68: This is a long text line number 68 with searchable content spread across the file.
|
||||
Line 69: This is a long text line number 69 with searchable content spread across the file.
|
||||
Line 70: This is a long text line number 70 with searchable content spread across the file.
|
||||
Line 71: This is a long text line number 71 with searchable content spread across the file.
|
||||
Line 72: This is a long text line number 72 with searchable content spread across the file.
|
||||
Line 73: This is a long text line number 73 with searchable content spread across the file.
|
||||
Line 74: This is a long text line number 74 with searchable content spread across the file.
|
||||
Line 75: This is a long text line number 75 with searchable content spread across the file.
|
||||
Line 76: This is a long text line number 76 with searchable content spread across the file.
|
||||
Line 77: This is a long text line number 77 with searchable content spread across the file.
|
||||
Line 78: This is a long text line number 78 with searchable content spread across the file.
|
||||
Line 79: This is a long text line number 79 with searchable content spread across the file.
|
||||
Line 80: This is a long text line number 80 with searchable content spread across the file.
|
||||
Line 81: This is a long text line number 81 with searchable content spread across the file.
|
||||
Line 82: This is a long text line number 82 with searchable content spread across the file.
|
||||
Line 83: This is a long text line number 83 with searchable content spread across the file.
|
||||
Line 84: This is a long text line number 84 with searchable content spread across the file.
|
||||
Line 85: This is a long text line number 85 with searchable content spread across the file.
|
||||
Line 86: This is a long text line number 86 with searchable content spread across the file.
|
||||
Line 87: This is a long text line number 87 with searchable content spread across the file.
|
||||
Line 88: This is a long text line number 88 with searchable content spread across the file.
|
||||
Line 89: This is a long text line number 89 with searchable content spread across the file.
|
||||
Line 90: This is a long text line number 90 with searchable content spread across the file.
|
||||
Line 91: This is a long text line number 91 with searchable content spread across the file.
|
||||
Line 92: This is a long text line number 92 with searchable content spread across the file.
|
||||
Line 93: This is a long text line number 93 with searchable content spread across the file.
|
||||
Line 94: This is a long text line number 94 with searchable content spread across the file.
|
||||
Line 95: This is a long text line number 95 with searchable content spread across the file.
|
||||
Line 96: This is a long text line number 96 with searchable content spread across the file.
|
||||
Line 97: This is a long text line number 97 with searchable content spread across the file.
|
||||
Line 98: This is a long text line number 98 with searchable content spread across the file.
|
||||
Line 99: This is a long text line number 99 with searchable content spread across the file.
|
||||
Line 100: This is a long text line number 100 with searchable content spread across the file.
|
||||
Line 101: This is a long text line number 101 with searchable content spread across the file.
|
||||
Line 102: This is a long text line number 102 with searchable content spread across the file.
|
||||
Line 103: This is a long text line number 103 with searchable content spread across the file.
|
||||
Line 104: This is a long text line number 104 with searchable content spread across the file.
|
||||
Line 105: This is a long text line number 105 with searchable content spread across the file.
|
||||
Line 106: This is a long text line number 106 with searchable content spread across the file.
|
||||
Line 107: This is a long text line number 107 with searchable content spread across the file.
|
||||
Line 108: This is a long text line number 108 with searchable content spread across the file.
|
||||
Line 109: This is a long text line number 109 with searchable content spread across the file.
|
||||
Line 110: This is a long text line number 110 with searchable content spread across the file.
|
||||
Line 111: This is a long text line number 111 with searchable content spread across the file.
|
||||
Line 112: This is a long text line number 112 with searchable content spread across the file.
|
||||
Line 113: This is a long text line number 113 with searchable content spread across the file.
|
||||
Line 114: This is a long text line number 114 with searchable content spread across the file.
|
||||
Line 115: This is a long text line number 115 with searchable content spread across the file.
|
||||
Line 116: This is a long text line number 116 with searchable content spread across the file.
|
||||
Line 117: This is a long text line number 117 with searchable content spread across the file.
|
||||
Line 118: This is a long text line number 118 with searchable content spread across the file.
|
||||
Line 119: This is a long text line number 119 with searchable content spread across the file.
|
||||
Line 120: This is a long text line number 120 with searchable content spread across the file.
|
||||
Line 121: This is a long text line number 121 with searchable content spread across the file.
|
||||
Line 122: This is a long text line number 122 with searchable content spread across the file.
|
||||
Line 123: This is a long text line number 123 with searchable content spread across the file.
|
||||
Line 124: This is a long text line number 124 with searchable content spread across the file.
|
||||
Line 125: This is a long text line number 125 with searchable content spread across the file.
|
||||
Line 126: This is a long text line number 126 with searchable content spread across the file.
|
||||
Line 127: This is a long text line number 127 with searchable content spread across the file.
|
||||
Line 128: This is a long text line number 128 with searchable content spread across the file.
|
||||
Line 129: This is a long text line number 129 with searchable content spread across the file.
|
||||
Line 130: This is a long text line number 130 with searchable content spread across the file.
|
||||
Line 131: This is a long text line number 131 with searchable content spread across the file.
|
||||
Line 132: This is a long text line number 132 with searchable content spread across the file.
|
||||
Line 133: This is a long text line number 133 with searchable content spread across the file.
|
||||
Line 134: This is a long text line number 134 with searchable content spread across the file.
|
||||
Line 135: This is a long text line number 135 with searchable content spread across the file.
|
||||
Line 136: This is a long text line number 136 with searchable content spread across the file.
|
||||
Line 137: This is a long text line number 137 with searchable content spread across the file.
|
||||
Line 138: This is a long text line number 138 with searchable content spread across the file.
|
||||
Line 139: This is a long text line number 139 with searchable content spread across the file.
|
||||
Line 140: This is a long text line number 140 with searchable content spread across the file.
|
||||
Line 141: This is a long text line number 141 with searchable content spread across the file.
|
||||
Line 142: This is a long text line number 142 with searchable content spread across the file.
|
||||
Line 143: This is a long text line number 143 with searchable content spread across the file.
|
||||
Line 144: This is a long text line number 144 with searchable content spread across the file.
|
||||
Line 145: This is a long text line number 145 with searchable content spread across the file.
|
||||
Line 146: This is a long text line number 146 with searchable content spread across the file.
|
||||
Line 147: This is a long text line number 147 with searchable content spread across the file.
|
||||
Line 148: This is a long text line number 148 with searchable content spread across the file.
|
||||
Line 149: This is a long text line number 149 with searchable content spread across the file.
|
||||
Line 150: This is a long text line number 150 with searchable content spread across the file.
|
||||
Line 151: This is a long text line number 151 with searchable content spread across the file.
|
||||
Line 152: This is a long text line number 152 with searchable content spread across the file.
|
||||
Line 153: This is a long text line number 153 with searchable content spread across the file.
|
||||
Line 154: This is a long text line number 154 with searchable content spread across the file.
|
||||
Line 155: This is a long text line number 155 with searchable content spread across the file.
|
||||
Line 156: This is a long text line number 156 with searchable content spread across the file.
|
||||
Line 157: This is a long text line number 157 with searchable content spread across the file.
|
||||
Line 158: This is a long text line number 158 with searchable content spread across the file.
|
||||
Line 159: This is a long text line number 159 with searchable content spread across the file.
|
||||
Line 160: This is a long text line number 160 with searchable content spread across the file.
|
||||
Line 161: This is a long text line number 161 with searchable content spread across the file.
|
||||
Line 162: This is a long text line number 162 with searchable content spread across the file.
|
||||
Line 163: This is a long text line number 163 with searchable content spread across the file.
|
||||
Line 164: This is a long text line number 164 with searchable content spread across the file.
|
||||
Line 165: This is a long text line number 165 with searchable content spread across the file.
|
||||
Line 166: This is a long text line number 166 with searchable content spread across the file.
|
||||
Line 167: This is a long text line number 167 with searchable content spread across the file.
|
||||
Line 168: This is a long text line number 168 with searchable content spread across the file.
|
||||
Line 169: This is a long text line number 169 with searchable content spread across the file.
|
||||
Line 170: This is a long text line number 170 with searchable content spread across the file.
|
||||
Line 171: This is a long text line number 171 with searchable content spread across the file.
|
||||
Line 172: This is a long text line number 172 with searchable content spread across the file.
|
||||
Line 173: This is a long text line number 173 with searchable content spread across the file.
|
||||
Line 174: This is a long text line number 174 with searchable content spread across the file.
|
||||
Line 175: This is a long text line number 175 with searchable content spread across the file.
|
||||
Line 176: This is a long text line number 176 with searchable content spread across the file.
|
||||
Line 177: This is a long text line number 177 with searchable content spread across the file.
|
||||
Line 178: This is a long text line number 178 with searchable content spread across the file.
|
||||
Line 179: This is a long text line number 179 with searchable content spread across the file.
|
||||
Line 180: This is a long text line number 180 with searchable content spread across the file.
|
||||
Line 181: This is a long text line number 181 with searchable content spread across the file.
|
||||
Line 182: This is a long text line number 182 with searchable content spread across the file.
|
||||
Line 183: This is a long text line number 183 with searchable content spread across the file.
|
||||
Line 184: This is a long text line number 184 with searchable content spread across the file.
|
||||
Line 185: This is a long text line number 185 with searchable content spread across the file.
|
||||
Line 186: This is a long text line number 186 with searchable content spread across the file.
|
||||
Line 187: This is a long text line number 187 with searchable content spread across the file.
|
||||
Line 188: This is a long text line number 188 with searchable content spread across the file.
|
||||
Line 189: This is a long text line number 189 with searchable content spread across the file.
|
||||
Line 190: This is a long text line number 190 with searchable content spread across the file.
|
||||
Line 191: This is a long text line number 191 with searchable content spread across the file.
|
||||
Line 192: This is a long text line number 192 with searchable content spread across the file.
|
||||
Line 193: This is a long text line number 193 with searchable content spread across the file.
|
||||
Line 194: This is a long text line number 194 with searchable content spread across the file.
|
||||
Line 195: This is a long text line number 195 with searchable content spread across the file.
|
||||
Line 196: This is a long text line number 196 with searchable content spread across the file.
|
||||
Line 197: This is a long text line number 197 with searchable content spread across the file.
|
||||
Line 198: This is a long text line number 198 with searchable content spread across the file.
|
||||
Line 199: This is a long text line number 199 with searchable content spread across the file.
|
||||
Line 200: This is a long text line number 200 with searchable content spread across the file.
|
||||
Line 201: This is a long text line number 201 with searchable content spread across the file.
|
||||
Line 202: This is a long text line number 202 with searchable content spread across the file.
|
||||
Line 203: This is a long text line number 203 with searchable content spread across the file.
|
||||
Line 204: This is a long text line number 204 with searchable content spread across the file.
|
||||
Line 205: This is a long text line number 205 with searchable content spread across the file.
|
||||
Line 206: This is a long text line number 206 with searchable content spread across the file.
|
||||
Line 207: This is a long text line number 207 with searchable content spread across the file.
|
||||
Line 208: This is a long text line number 208 with searchable content spread across the file.
|
||||
Line 209: This is a long text line number 209 with searchable content spread across the file.
|
||||
Line 210: This is a long text line number 210 with searchable content spread across the file.
|
||||
Line 211: This is a long text line number 211 with searchable content spread across the file.
|
||||
Line 212: This is a long text line number 212 with searchable content spread across the file.
|
||||
Line 213: This is a long text line number 213 with searchable content spread across the file.
|
||||
Line 214: This is a long text line number 214 with searchable content spread across the file.
|
||||
Line 215: This is a long text line number 215 with searchable content spread across the file.
|
||||
Line 216: This is a long text line number 216 with searchable content spread across the file.
|
||||
Line 217: This is a long text line number 217 with searchable content spread across the file.
|
||||
Line 218: This is a long text line number 218 with searchable content spread across the file.
|
||||
Line 219: This is a long text line number 219 with searchable content spread across the file.
|
||||
Line 220: This is a long text line number 220 with searchable content spread across the file.
|
||||
Line 221: This is a long text line number 221 with searchable content spread across the file.
|
||||
Line 222: This is a long text line number 222 with searchable content spread across the file.
|
||||
Line 223: This is a long text line number 223 with searchable content spread across the file.
|
||||
Line 224: This is a long text line number 224 with searchable content spread across the file.
|
||||
Line 225: This is a long text line number 225 with searchable content spread across the file.
|
||||
Line 226: This is a long text line number 226 with searchable content spread across the file.
|
||||
Line 227: This is a long text line number 227 with searchable content spread across the file.
|
||||
Line 228: This is a long text line number 228 with searchable content spread across the file.
|
||||
Line 229: This is a long text line number 229 with searchable content spread across the file.
|
||||
Line 230: This is a long text line number 230 with searchable content spread across the file.
|
||||
Line 231: This is a long text line number 231 with searchable content spread across the file.
|
||||
Line 232: This is a long text line number 232 with searchable content spread across the file.
|
||||
Line 233: This is a long text line number 233 with searchable content spread across the file.
|
||||
Line 234: This is a long text line number 234 with searchable content spread across the file.
|
||||
Line 235: This is a long text line number 235 with searchable content spread across the file.
|
||||
Line 236: This is a long text line number 236 with searchable content spread across the file.
|
||||
Line 237: This is a long text line number 237 with searchable content spread across the file.
|
||||
Line 238: This is a long text line number 238 with searchable content spread across the file.
|
||||
Line 239: This is a long text line number 239 with searchable content spread across the file.
|
||||
Line 240: This is a long text line number 240 with searchable content spread across the file.
|
||||
Line 241: This is a long text line number 241 with searchable content spread across the file.
|
||||
Line 242: This is a long text line number 242 with searchable content spread across the file.
|
||||
Line 243: This is a long text line number 243 with searchable content spread across the file.
|
||||
Line 244: This is a long text line number 244 with searchable content spread across the file.
|
||||
Line 245: This is a long text line number 245 with searchable content spread across the file.
|
||||
Line 246: This is a long text line number 246 with searchable content spread across the file.
|
||||
Line 247: This is a long text line number 247 with searchable content spread across the file.
|
||||
Line 248: This is a long text line number 248 with searchable content spread across the file.
|
||||
Line 249: This is a long text line number 249 with searchable content spread across the file.
|
||||
Line 250: This is a long text line number 250 with searchable content spread across the file.
|
||||
Line 251: This is a long text line number 251 with searchable content spread across the file.
|
||||
Line 252: This is a long text line number 252 with searchable content spread across the file.
|
||||
Line 253: This is a long text line number 253 with searchable content spread across the file.
|
||||
Line 254: This is a long text line number 254 with searchable content spread across the file.
|
||||
Line 255: This is a long text line number 255 with searchable content spread across the file.
|
||||
Line 256: This is a long text line number 256 with searchable content spread across the file.
|
||||
Line 257: This is a long text line number 257 with searchable content spread across the file.
|
||||
Line 258: This is a long text line number 258 with searchable content spread across the file.
|
||||
Line 259: This is a long text line number 259 with searchable content spread across the file.
|
||||
Line 260: This is a long text line number 260 with searchable content spread across the file.
|
||||
Line 261: This is a long text line number 261 with searchable content spread across the file.
|
||||
Line 262: This is a long text line number 262 with searchable content spread across the file.
|
||||
Line 263: This is a long text line number 263 with searchable content spread across the file.
|
||||
Line 264: This is a long text line number 264 with searchable content spread across the file.
|
||||
Line 265: This is a long text line number 265 with searchable content spread across the file.
|
||||
Line 266: This is a long text line number 266 with searchable content spread across the file.
|
||||
Line 267: This is a long text line number 267 with searchable content spread across the file.
|
||||
Line 268: This is a long text line number 268 with searchable content spread across the file.
|
||||
Line 269: This is a long text line number 269 with searchable content spread across the file.
|
||||
Line 270: This is a long text line number 270 with searchable content spread across the file.
|
||||
Line 271: This is a long text line number 271 with searchable content spread across the file.
|
||||
Line 272: This is a long text line number 272 with searchable content spread across the file.
|
||||
Line 273: This is a long text line number 273 with searchable content spread across the file.
|
||||
Line 274: This is a long text line number 274 with searchable content spread across the file.
|
||||
Line 275: This is a long text line number 275 with searchable content spread across the file.
|
||||
Line 276: This is a long text line number 276 with searchable content spread across the file.
|
||||
Line 277: This is a long text line number 277 with searchable content spread across the file.
|
||||
Line 278: This is a long text line number 278 with searchable content spread across the file.
|
||||
Line 279: This is a long text line number 279 with searchable content spread across the file.
|
||||
Line 280: This is a long text line number 280 with searchable content spread across the file.
|
||||
Line 281: This is a long text line number 281 with searchable content spread across the file.
|
||||
Line 282: This is a long text line number 282 with searchable content spread across the file.
|
||||
Line 283: This is a long text line number 283 with searchable content spread across the file.
|
||||
Line 284: This is a long text line number 284 with searchable content spread across the file.
|
||||
Line 285: This is a long text line number 285 with searchable content spread across the file.
|
||||
Line 286: This is a long text line number 286 with searchable content spread across the file.
|
||||
Line 287: This is a long text line number 287 with searchable content spread across the file.
|
||||
Line 288: This is a long text line number 288 with searchable content spread across the file.
|
||||
Line 289: This is a long text line number 289 with searchable content spread across the file.
|
||||
Line 290: This is a long text line number 290 with searchable content spread across the file.
|
||||
Line 291: This is a long text line number 291 with searchable content spread across the file.
|
||||
Line 292: This is a long text line number 292 with searchable content spread across the file.
|
||||
Line 293: This is a long text line number 293 with searchable content spread across the file.
|
||||
Line 294: This is a long text line number 294 with searchable content spread across the file.
|
||||
Line 295: This is a long text line number 295 with searchable content spread across the file.
|
||||
Line 296: This is a long text line number 296 with searchable content spread across the file.
|
||||
Line 297: This is a long text line number 297 with searchable content spread across the file.
|
||||
Line 298: This is a long text line number 298 with searchable content spread across the file.
|
||||
Line 299: This is a long text line number 299 with searchable content spread across the file.
|
||||
Line 300: This is a long text line number 300 with searchable content spread across the file.
|
||||
Line 301: This is a long text line number 301 with searchable content spread across the file.
|
||||
Line 302: This is a long text line number 302 with searchable content spread across the file.
|
||||
Line 303: This is a long text line number 303 with searchable content spread across the file.
|
||||
Line 304: This is a long text line number 304 with searchable content spread across the file.
|
||||
Line 305: This is a long text line number 305 with searchable content spread across the file.
|
||||
Line 306: This is a long text line number 306 with searchable content spread across the file.
|
||||
Line 307: This is a long text line number 307 with searchable content spread across the file.
|
||||
Line 308: This is a long text line number 308 with searchable content spread across the file.
|
||||
Line 309: This is a long text line number 309 with searchable content spread across the file.
|
||||
Line 310: This is a long text line number 310 with searchable content spread across the file.
|
||||
Line 311: This is a long text line number 311 with searchable content spread across the file.
|
||||
Line 312: This is a long text line number 312 with searchable content spread across the file.
|
||||
Line 313: This is a long text line number 313 with searchable content spread across the file.
|
||||
Line 314: This is a long text line number 314 with searchable content spread across the file.
|
||||
Line 315: This is a long text line number 315 with searchable content spread across the file.
|
||||
Line 316: This is a long text line number 316 with searchable content spread across the file.
|
||||
Line 317: This is a long text line number 317 with searchable content spread across the file.
|
||||
Line 318: This is a long text line number 318 with searchable content spread across the file.
|
||||
Line 319: This is a long text line number 319 with searchable content spread across the file.
|
||||
Line 320: This is a long text line number 320 with searchable content spread across the file.
|
||||
Line 321: This is a long text line number 321 with searchable content spread across the file.
|
||||
Line 322: This is a long text line number 322 with searchable content spread across the file.
|
||||
Line 323: This is a long text line number 323 with searchable content spread across the file.
|
||||
Line 324: This is a long text line number 324 with searchable content spread across the file.
|
||||
Line 325: This is a long text line number 325 with searchable content spread across the file.
|
||||
Line 326: This is a long text line number 326 with searchable content spread across the file.
|
||||
Line 327: This is a long text line number 327 with searchable content spread across the file.
|
||||
Line 328: This is a long text line number 328 with searchable content spread across the file.
|
||||
Line 329: This is a long text line number 329 with searchable content spread across the file.
|
||||
Line 330: This is a long text line number 330 with searchable content spread across the file.
|
||||
Line 331: This is a long text line number 331 with searchable content spread across the file.
|
||||
Line 332: This is a long text line number 332 with searchable content spread across the file.
|
||||
Line 333: This is a long text line number 333 with searchable content spread across the file.
|
||||
Line 334: This is a long text line number 334 with searchable content spread across the file.
|
||||
Line 335: This is a long text line number 335 with searchable content spread across the file.
|
||||
Line 336: This is a long text line number 336 with searchable content spread across the file.
|
||||
Line 337: This is a long text line number 337 with searchable content spread across the file.
|
||||
Line 338: This is a long text line number 338 with searchable content spread across the file.
|
||||
Line 339: This is a long text line number 339 with searchable content spread across the file.
|
||||
Line 340: This is a long text line number 340 with searchable content spread across the file.
|
||||
Line 341: This is a long text line number 341 with searchable content spread across the file.
|
||||
Line 342: This is a long text line number 342 with searchable content spread across the file.
|
||||
Line 343: This is a long text line number 343 with searchable content spread across the file.
|
||||
Line 344: This is a long text line number 344 with searchable content spread across the file.
|
||||
Line 345: This is a long text line number 345 with searchable content spread across the file.
|
||||
Line 346: This is a long text line number 346 with searchable content spread across the file.
|
||||
Line 347: This is a long text line number 347 with searchable content spread across the file.
|
||||
Line 348: This is a long text line number 348 with searchable content spread across the file.
|
||||
Line 349: This is a long text line number 349 with searchable content spread across the file.
|
||||
Line 350: This is a long text line number 350 with searchable content spread across the file.
|
||||
Line 351: This is a long text line number 351 with searchable content spread across the file.
|
||||
Line 352: This is a long text line number 352 with searchable content spread across the file.
|
||||
Line 353: This is a long text line number 353 with searchable content spread across the file.
|
||||
Line 354: This is a long text line number 354 with searchable content spread across the file.
|
||||
Line 355: This is a long text line number 355 with searchable content spread across the file.
|
||||
Line 356: This is a long text line number 356 with searchable content spread across the file.
|
||||
Line 357: This is a long text line number 357 with searchable content spread across the file.
|
||||
Line 358: This is a long text line number 358 with searchable content spread across the file.
|
||||
Line 359: This is a long text line number 359 with searchable content spread across the file.
|
||||
Line 360: This is a long text line number 360 with searchable content spread across the file.
|
||||
Line 361: This is a long text line number 361 with searchable content spread across the file.
|
||||
Line 362: This is a long text line number 362 with searchable content spread across the file.
|
||||
Line 363: This is a long text line number 363 with searchable content spread across the file.
|
||||
Line 364: This is a long text line number 364 with searchable content spread across the file.
|
||||
Line 365: This is a long text line number 365 with searchable content spread across the file.
|
||||
Line 366: This is a long text line number 366 with searchable content spread across the file.
|
||||
Line 367: This is a long text line number 367 with searchable content spread across the file.
|
||||
Line 368: This is a long text line number 368 with searchable content spread across the file.
|
||||
Line 369: This is a long text line number 369 with searchable content spread across the file.
|
||||
Line 370: This is a long text line number 370 with searchable content spread across the file.
|
||||
Line 371: This is a long text line number 371 with searchable content spread across the file.
|
||||
Line 372: This is a long text line number 372 with searchable content spread across the file.
|
||||
Line 373: This is a long text line number 373 with searchable content spread across the file.
|
||||
Line 374: This is a long text line number 374 with searchable content spread across the file.
|
||||
Line 375: This is a long text line number 375 with searchable content spread across the file.
|
||||
Line 376: This is a long text line number 376 with searchable content spread across the file.
|
||||
Line 377: This is a long text line number 377 with searchable content spread across the file.
|
||||
Line 378: This is a long text line number 378 with searchable content spread across the file.
|
||||
Line 379: This is a long text line number 379 with searchable content spread across the file.
|
||||
Line 380: This is a long text line number 380 with searchable content spread across the file.
|
||||
Line 381: This is a long text line number 381 with searchable content spread across the file.
|
||||
Line 382: This is a long text line number 382 with searchable content spread across the file.
|
||||
Line 383: This is a long text line number 383 with searchable content spread across the file.
|
||||
Line 384: This is a long text line number 384 with searchable content spread across the file.
|
||||
Line 385: This is a long text line number 385 with searchable content spread across the file.
|
||||
Line 386: This is a long text line number 386 with searchable content spread across the file.
|
||||
Line 387: This is a long text line number 387 with searchable content spread across the file.
|
||||
Line 388: This is a long text line number 388 with searchable content spread across the file.
|
||||
Line 389: This is a long text line number 389 with searchable content spread across the file.
|
||||
Line 390: This is a long text line number 390 with searchable content spread across the file.
|
||||
Line 391: This is a long text line number 391 with searchable content spread across the file.
|
||||
Line 392: This is a long text line number 392 with searchable content spread across the file.
|
||||
Line 393: This is a long text line number 393 with searchable content spread across the file.
|
||||
Line 394: This is a long text line number 394 with searchable content spread across the file.
|
||||
Line 395: This is a long text line number 395 with searchable content spread across the file.
|
||||
Line 396: This is a long text line number 396 with searchable content spread across the file.
|
||||
Line 397: This is a long text line number 397 with searchable content spread across the file.
|
||||
Line 398: This is a long text line number 398 with searchable content spread across the file.
|
||||
Line 399: This is a long text line number 399 with searchable content spread across the file.
|
||||
Line 400: This is a long text line number 400 with searchable content spread across the file.
|
||||
Line 401: This is a long text line number 401 with searchable content spread across the file.
|
||||
Line 402: This is a long text line number 402 with searchable content spread across the file.
|
||||
Line 403: This is a long text line number 403 with searchable content spread across the file.
|
||||
Line 404: This is a long text line number 404 with searchable content spread across the file.
|
||||
Line 405: This is a long text line number 405 with searchable content spread across the file.
|
||||
Line 406: This is a long text line number 406 with searchable content spread across the file.
|
||||
Line 407: This is a long text line number 407 with searchable content spread across the file.
|
||||
Line 408: This is a long text line number 408 with searchable content spread across the file.
|
||||
Line 409: This is a long text line number 409 with searchable content spread across the file.
|
||||
Line 410: This is a long text line number 410 with searchable content spread across the file.
|
||||
Line 411: This is a long text line number 411 with searchable content spread across the file.
|
||||
Line 412: This is a long text line number 412 with searchable content spread across the file.
|
||||
Line 413: This is a long text line number 413 with searchable content spread across the file.
|
||||
Line 414: This is a long text line number 414 with searchable content spread across the file.
|
||||
Line 415: This is a long text line number 415 with searchable content spread across the file.
|
||||
Line 416: This is a long text line number 416 with searchable content spread across the file.
|
||||
Line 417: This is a long text line number 417 with searchable content spread across the file.
|
||||
Line 418: This is a long text line number 418 with searchable content spread across the file.
|
||||
Line 419: This is a long text line number 419 with searchable content spread across the file.
|
||||
Line 420: This is a long text line number 420 with searchable content spread across the file.
|
||||
Line 421: This is a long text line number 421 with searchable content spread across the file.
|
||||
Line 422: This is a long text line number 422 with searchable content spread across the file.
|
||||
Line 423: This is a long text line number 423 with searchable content spread across the file.
|
||||
Line 424: This is a long text line number 424 with searchable content spread across the file.
|
||||
Line 425: This is a long text line number 425 with searchable content spread across the file.
|
||||
Line 426: This is a long text line number 426 with searchable content spread across the file.
|
||||
Line 427: This is a long text line number 427 with searchable content spread across the file.
|
||||
Line 428: This is a long text line number 428 with searchable content spread across the file.
|
||||
Line 429: This is a long text line number 429 with searchable content spread across the file.
|
||||
Line 430: This is a long text line number 430 with searchable content spread across the file.
|
||||
Line 431: This is a long text line number 431 with searchable content spread across the file.
|
||||
Line 432: This is a long text line number 432 with searchable content spread across the file.
|
||||
Line 433: This is a long text line number 433 with searchable content spread across the file.
|
||||
Line 434: This is a long text line number 434 with searchable content spread across the file.
|
||||
Line 435: This is a long text line number 435 with searchable content spread across the file.
|
||||
Line 436: This is a long text line number 436 with searchable content spread across the file.
|
||||
Line 437: This is a long text line number 437 with searchable content spread across the file.
|
||||
Line 438: This is a long text line number 438 with searchable content spread across the file.
|
||||
Line 439: This is a long text line number 439 with searchable content spread across the file.
|
||||
Line 440: This is a long text line number 440 with searchable content spread across the file.
|
||||
Line 441: This is a long text line number 441 with searchable content spread across the file.
|
||||
Line 442: This is a long text line number 442 with searchable content spread across the file.
|
||||
Line 443: This is a long text line number 443 with searchable content spread across the file.
|
||||
Line 444: This is a long text line number 444 with searchable content spread across the file.
|
||||
Line 445: This is a long text line number 445 with searchable content spread across the file.
|
||||
Line 446: This is a long text line number 446 with searchable content spread across the file.
|
||||
Line 447: This is a long text line number 447 with searchable content spread across the file.
|
||||
Line 448: This is a long text line number 448 with searchable content spread across the file.
|
||||
Line 449: This is a long text line number 449 with searchable content spread across the file.
|
||||
Line 450: This is a long text line number 450 with searchable content spread across the file.
|
||||
Line 451: This is a long text line number 451 with searchable content spread across the file.
|
||||
Line 452: This is a long text line number 452 with searchable content spread across the file.
|
||||
Line 453: This is a long text line number 453 with searchable content spread across the file.
|
||||
Line 454: This is a long text line number 454 with searchable content spread across the file.
|
||||
Line 455: This is a long text line number 455 with searchable content spread across the file.
|
||||
Line 456: This is a long text line number 456 with searchable content spread across the file.
|
||||
Line 457: This is a long text line number 457 with searchable content spread across the file.
|
||||
Line 458: This is a long text line number 458 with searchable content spread across the file.
|
||||
Line 459: This is a long text line number 459 with searchable content spread across the file.
|
||||
Line 460: This is a long text line number 460 with searchable content spread across the file.
|
||||
Line 461: This is a long text line number 461 with searchable content spread across the file.
|
||||
Line 462: This is a long text line number 462 with searchable content spread across the file.
|
||||
Line 463: This is a long text line number 463 with searchable content spread across the file.
|
||||
Line 464: This is a long text line number 464 with searchable content spread across the file.
|
||||
Line 465: This is a long text line number 465 with searchable content spread across the file.
|
||||
Line 466: This is a long text line number 466 with searchable content spread across the file.
|
||||
Line 467: This is a long text line number 467 with searchable content spread across the file.
|
||||
Line 468: This is a long text line number 468 with searchable content spread across the file.
|
||||
Line 469: This is a long text line number 469 with searchable content spread across the file.
|
||||
Line 470: This is a long text line number 470 with searchable content spread across the file.
|
||||
Line 471: This is a long text line number 471 with searchable content spread across the file.
|
||||
Line 472: This is a long text line number 472 with searchable content spread across the file.
|
||||
Line 473: This is a long text line number 473 with searchable content spread across the file.
|
||||
Line 474: This is a long text line number 474 with searchable content spread across the file.
|
||||
Line 475: This is a long text line number 475 with searchable content spread across the file.
|
||||
Line 476: This is a long text line number 476 with searchable content spread across the file.
|
||||
Line 477: This is a long text line number 477 with searchable content spread across the file.
|
||||
Line 478: This is a long text line number 478 with searchable content spread across the file.
|
||||
Line 479: This is a long text line number 479 with searchable content spread across the file.
|
||||
Line 480: This is a long text line number 480 with searchable content spread across the file.
|
||||
Line 481: This is a long text line number 481 with searchable content spread across the file.
|
||||
Line 482: This is a long text line number 482 with searchable content spread across the file.
|
||||
Line 483: This is a long text line number 483 with searchable content spread across the file.
|
||||
Line 484: This is a long text line number 484 with searchable content spread across the file.
|
||||
Line 485: This is a long text line number 485 with searchable content spread across the file.
|
||||
Line 486: This is a long text line number 486 with searchable content spread across the file.
|
||||
Line 487: This is a long text line number 487 with searchable content spread across the file.
|
||||
Line 488: This is a long text line number 488 with searchable content spread across the file.
|
||||
Line 489: This is a long text line number 489 with searchable content spread across the file.
|
||||
Line 490: This is a long text line number 490 with searchable content spread across the file.
|
||||
Line 491: This is a long text line number 491 with searchable content spread across the file.
|
||||
Line 492: This is a long text line number 492 with searchable content spread across the file.
|
||||
Line 493: This is a long text line number 493 with searchable content spread across the file.
|
||||
Line 494: This is a long text line number 494 with searchable content spread across the file.
|
||||
Line 495: This is a long text line number 495 with searchable content spread across the file.
|
||||
Line 496: This is a long text line number 496 with searchable content spread across the file.
|
||||
Line 497: This is a long text line number 497 with searchable content spread across the file.
|
||||
Line 498: This is a long text line number 498 with searchable content spread across the file.
|
||||
Line 499: This is a long text line number 499 with searchable content spread across the file.
|
||||
Line 500: This is a long text line number 500 with searchable content spread across the file.
|
||||
Loading…
x
Reference in New Issue
Block a user