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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||||
#define 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
|
void uniffi_zx_document_ffi_fn_func_clear_exported_events(uint32_t count, RustCallStatus *_Nonnull out_status
|
||||||
);
|
);
|
||||||
#endif
|
#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
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||||
#define 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
|
RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||||
);
|
);
|
||||||
#endif
|
#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
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||||
#define 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
|
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
|
#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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN
|
#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
|
RustBuffer uniffi_zx_document_ffi_fn_func_parse_text(RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||||
);
|
);
|
||||||
#endif
|
#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
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||||
#define 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
|
void uniffi_zx_document_ffi_fn_func_push_reading_event(RustBuffer event, RustCallStatus *_Nonnull out_status
|
||||||
);
|
);
|
||||||
#endif
|
#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
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||||
#define 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
|
RustBuffer uniffi_zx_document_ffi_fn_func_read_image_meta(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||||
);
|
);
|
||||||
#endif
|
#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
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||||
#define 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
|
RustBuffer uniffi_zx_document_ffi_fn_func_read_text_stats(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||||
);
|
);
|
||||||
#endif
|
#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
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||||
#define 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
|
RustBuffer uniffi_zx_document_ffi_fn_func_search_markdown_blocks(RustBuffer blocks, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||||
);
|
);
|
||||||
#endif
|
#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
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||||
#define 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
|
RustBuffer uniffi_zx_document_ffi_fn_func_search_text_content(RustBuffer content, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||||
);
|
);
|
||||||
#endif
|
#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
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||||
#define 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
|
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
|
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||||
#define 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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||||
#define 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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||||
#define 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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE
|
#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
|
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS
|
||||||
uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events(void
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN
|
#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
|
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT
|
||||||
uint16_t uniffi_zx_document_ffi_checksum_func_parse_text(void
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||||
#define 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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||||
#define 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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||||
#define 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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||||
#define 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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||||
#define 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
|
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
|
#endif
|
||||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION
|
#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.
@ -16,8 +16,15 @@ fn main() {
|
|||||||
Some("fixtures") => {
|
Some("fixtures") => {
|
||||||
println!("fixtures/");
|
println!("fixtures/");
|
||||||
println!(" markdown/sample.md — all block types");
|
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/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!(" 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") => {
|
Some("verify-ios") => {
|
||||||
let root = project_root();
|
let root = project_root();
|
||||||
|
|||||||
@ -10,6 +10,7 @@ infer = "0.16"
|
|||||||
mime_guess = "2"
|
mime_guess = "2"
|
||||||
comrak = "0.29"
|
comrak = "0.29"
|
||||||
uuid = { version = "1", features = ["v4"] }
|
uuid = { version = "1", features = ["v4"] }
|
||||||
|
zip = "2"
|
||||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif"] }
|
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif"] }
|
||||||
uniffi = "0.31"
|
uniffi = "0.31"
|
||||||
|
|
||||||
|
|||||||
@ -1,71 +1,188 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::progress::ReadingPosition;
|
||||||
|
use crate::search::SearchResult;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, uniffi::Enum)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, uniffi::Enum)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum NoteAnchor {
|
pub enum NoteAnchor {
|
||||||
Material {
|
Material {
|
||||||
material_id: String,
|
material_id: String,
|
||||||
|
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||||
|
position_snapshot: Option<ReadingPosition>,
|
||||||
},
|
},
|
||||||
MarkdownBlock {
|
MarkdownBlock {
|
||||||
material_id: String,
|
material_id: String,
|
||||||
block_id: String,
|
block_id: String,
|
||||||
|
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||||
|
position_snapshot: Option<ReadingPosition>,
|
||||||
},
|
},
|
||||||
TextLine {
|
TextLine {
|
||||||
material_id: String,
|
material_id: String,
|
||||||
line_number: u32,
|
line_number: u32,
|
||||||
|
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||||
|
position_snapshot: Option<ReadingPosition>,
|
||||||
},
|
},
|
||||||
PdfPage {
|
PdfPage {
|
||||||
material_id: String,
|
material_id: String,
|
||||||
page_number: u32,
|
page_number: u32,
|
||||||
|
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||||
|
position_snapshot: Option<ReadingPosition>,
|
||||||
},
|
},
|
||||||
Image {
|
Image {
|
||||||
material_id: String,
|
material_id: String,
|
||||||
|
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||||
|
position_snapshot: Option<ReadingPosition>,
|
||||||
},
|
},
|
||||||
EpubChapter {
|
EpubChapter {
|
||||||
material_id: String,
|
material_id: String,
|
||||||
chapter_id: String,
|
chapter_id: String,
|
||||||
|
#[serde(rename = "positionSnapshot", skip_serializing_if = "Option::is_none", default)]
|
||||||
|
position_snapshot: Option<ReadingPosition>,
|
||||||
},
|
},
|
||||||
KnowledgeItem {
|
KnowledgeItem {
|
||||||
knowledge_item_id: String,
|
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 {
|
impl NoteAnchor {
|
||||||
/// Create a NoteAnchor from a material_id and optional ReadingPosition.
|
pub fn from_position(material_id: &str, position: Option<&ReadingPosition>) -> Self {
|
||||||
pub fn from_position(material_id: &str, position: Option<&crate::progress::ReadingPosition>) -> Self {
|
|
||||||
match position {
|
match position {
|
||||||
Some(crate::progress::ReadingPosition::Markdown { block_id, .. }) => {
|
Some(pos @ ReadingPosition::Markdown { block_id, .. }) => {
|
||||||
NoteAnchor::MarkdownBlock {
|
NoteAnchor::MarkdownBlock {
|
||||||
material_id: material_id.to_string(),
|
material_id: material_id.to_string(),
|
||||||
block_id: block_id.clone(),
|
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 {
|
NoteAnchor::TextLine {
|
||||||
material_id: material_id.to_string(),
|
material_id: material_id.to_string(),
|
||||||
line_number: *line_number,
|
line_number: *line_number,
|
||||||
|
position_snapshot: Some(pos.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(crate::progress::ReadingPosition::Pdf { page_number, .. }) => {
|
Some(pos @ ReadingPosition::Pdf { page_number, .. }) => {
|
||||||
NoteAnchor::PdfPage {
|
NoteAnchor::PdfPage {
|
||||||
material_id: material_id.to_string(),
|
material_id: material_id.to_string(),
|
||||||
page_number: *page_number,
|
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(),
|
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 {
|
NoteAnchor::EpubChapter {
|
||||||
material_id: material_id.to_string(),
|
material_id: material_id.to_string(),
|
||||||
chapter_id: chapter_id.clone(),
|
chapter_id: chapter_id.clone(),
|
||||||
|
position_snapshot: Some(pos.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => NoteAnchor::Material {
|
_ => NoteAnchor::Material {
|
||||||
material_id: material_id.to_string(),
|
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)]
|
#[cfg(test)]
|
||||||
@ -76,35 +193,303 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_material_anchor() {
|
fn test_material_anchor() {
|
||||||
let a = NoteAnchor::from_position("abc", None);
|
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]
|
#[test]
|
||||||
fn test_markdown_anchor() {
|
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));
|
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]
|
#[test]
|
||||||
fn test_pdf_anchor() {
|
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));
|
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]
|
#[test]
|
||||||
fn test_anchor_serde() {
|
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 json = serde_json::to_string(&a).unwrap();
|
||||||
let back: NoteAnchor = serde_json::from_str(&json).unwrap();
|
let back: NoteAnchor = serde_json::from_str(&json).unwrap();
|
||||||
assert_eq!(back, a);
|
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]
|
#[test]
|
||||||
fn test_unknown_position_falls_back_to_material() {
|
fn test_unknown_position_falls_back_to_material() {
|
||||||
let pos = ReadingPosition::Unknown;
|
let pos = ReadingPosition::Unknown;
|
||||||
let a = NoteAnchor::from_position("abc", Some(&pos));
|
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).
|
use std::fs;
|
||||||
// Rust Core will parse the EPUB container and expose chapter-level metadata.
|
use std::io::Read;
|
||||||
// Chapter HTML rendering is delegated to the host app's WebView / native HTML.
|
use std::path::Path;
|
||||||
// See #25 DOC-501 and #26 DOC-502 for design and implementation plan.
|
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::reading_material::ReadingMaterialRef;
|
use crate::reading_material::ReadingMaterialRef;
|
||||||
|
|
||||||
|
static TEST_LOCK: Mutex<()> = Mutex::new(());
|
||||||
|
|
||||||
fn setup_session(material_id: &str) -> String {
|
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 mat = ReadingMaterialRef::new(material_id);
|
||||||
let id = session_v2::start_reading_session_v2(mat, 1000).unwrap();
|
let id = session_v2::start_reading_session_v2(mat, 1000).unwrap();
|
||||||
clear_all_events_v2();
|
clear_all_events_v2();
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lock_test() -> std::sync::MutexGuard<'static, ()> {
|
||||||
|
TEST_LOCK.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn teardown_session(id: &str) {
|
fn teardown_session(id: &str) {
|
||||||
let _ = session_v2::close_reading_session_v2(id);
|
let _ = session_v2::close_reading_session_v2(id);
|
||||||
let _ = session_v2::remove_session_v2(id);
|
let _ = session_v2::remove_session_v2(id);
|
||||||
@ -255,6 +264,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_push_opened() {
|
fn test_push_opened() {
|
||||||
|
let _guard = lock_test();
|
||||||
let sid = setup_session("mat_1");
|
let sid = setup_session("mat_1");
|
||||||
let ev = push_material_opened_v2(&sid, "mat_1", 1000).unwrap();
|
let ev = push_material_opened_v2(&sid, "mat_1", 1000).unwrap();
|
||||||
assert_eq!(ev.event_type, ReadingEventTypeV2::MaterialOpened);
|
assert_eq!(ev.event_type, ReadingEventTypeV2::MaterialOpened);
|
||||||
@ -265,6 +275,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_push_closed() {
|
fn test_push_closed() {
|
||||||
|
let _guard = lock_test();
|
||||||
let sid = setup_session("mat_2");
|
let sid = setup_session("mat_2");
|
||||||
let ev = push_material_closed_v2(&sid, "mat_2", 0, 5000).unwrap();
|
let ev = push_material_closed_v2(&sid, "mat_2", 0, 5000).unwrap();
|
||||||
assert_eq!(ev.event_type, ReadingEventTypeV2::MaterialClosed);
|
assert_eq!(ev.event_type, ReadingEventTypeV2::MaterialClosed);
|
||||||
@ -273,6 +284,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sequence_increments() {
|
fn test_sequence_increments() {
|
||||||
|
let _guard = lock_test();
|
||||||
let sid = setup_session("mat_seq");
|
let sid = setup_session("mat_seq");
|
||||||
let e1 = push_material_opened_v2(&sid, "mat_seq", 1000).unwrap();
|
let e1 = push_material_opened_v2(&sid, "mat_seq", 1000).unwrap();
|
||||||
// Use delta=0 for tracker-independent tests
|
// Use delta=0 for tracker-independent tests
|
||||||
@ -286,6 +298,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_export_ack_flow() {
|
fn test_export_ack_flow() {
|
||||||
|
let _guard = TEST_LOCK.lock().unwrap();
|
||||||
clear_all_events_v2();
|
clear_all_events_v2();
|
||||||
let sid = setup_session("mat_ack");
|
let sid = setup_session("mat_ack");
|
||||||
let e1 = push_material_opened_v2(&sid, "mat_ack", 1000).unwrap();
|
let e1 = push_material_opened_v2(&sid, "mat_ack", 1000).unwrap();
|
||||||
@ -310,6 +323,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mark_failed_and_retry() {
|
fn test_mark_failed_and_retry() {
|
||||||
|
let _guard = TEST_LOCK.lock().unwrap();
|
||||||
clear_all_events_v2();
|
clear_all_events_v2();
|
||||||
let sid = setup_session("mat_retry");
|
let sid = setup_session("mat_retry");
|
||||||
let e1 = push_heartbeat_v2(&sid, "mat_retry", 15, None, 1000).unwrap();
|
let e1 = push_heartbeat_v2(&sid, "mat_retry", 15, None, 1000).unwrap();
|
||||||
@ -334,6 +348,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_buffer_size_not_exceeded() {
|
fn test_buffer_size_not_exceeded() {
|
||||||
|
let _guard = TEST_LOCK.lock().unwrap();
|
||||||
clear_all_events_v2();
|
clear_all_events_v2();
|
||||||
let sid = setup_session("mat_overflow");
|
let sid = setup_session("mat_overflow");
|
||||||
let sz = buffer_size_v2();
|
let sz = buffer_size_v2();
|
||||||
@ -348,6 +363,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ack_nonexistent_no_crash() {
|
fn test_ack_nonexistent_no_crash() {
|
||||||
|
let _guard = lock_test();
|
||||||
clear_all_events_v2();
|
clear_all_events_v2();
|
||||||
let removed = ack_events_v2(&["nonexistent".to_string()]);
|
let removed = ack_events_v2(&["nonexistent".to_string()]);
|
||||||
assert_eq!(removed, 0);
|
assert_eq!(removed, 0);
|
||||||
@ -355,6 +371,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_closed_session_rejects() {
|
fn test_closed_session_rejects() {
|
||||||
|
let _guard = lock_test();
|
||||||
let sid = setup_session("mat_reject");
|
let sid = setup_session("mat_reject");
|
||||||
session_v2::close_reading_session_v2(&sid).unwrap();
|
session_v2::close_reading_session_v2(&sid).unwrap();
|
||||||
assert!(push_heartbeat_v2(&sid, "mat_reject", 15, None, 3000).is_err());
|
assert!(push_heartbeat_v2(&sid, "mat_reject", 15, None, 3000).is_err());
|
||||||
@ -363,6 +380,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde_roundtrip() {
|
fn test_serde_roundtrip() {
|
||||||
|
let _guard = lock_test();
|
||||||
let sid = setup_session("mat_serde");
|
let sid = setup_session("mat_serde");
|
||||||
let ev = push_material_opened_v2(&sid, "mat_serde", 1000).unwrap();
|
let ev = push_material_opened_v2(&sid, "mat_serde", 1000).unwrap();
|
||||||
let json = serde_json::to_string(&ev).unwrap();
|
let json = serde_json::to_string(&ev).unwrap();
|
||||||
@ -371,4 +389,222 @@ mod tests {
|
|||||||
assert_eq!(back.event_id, ev.event_id);
|
assert_eq!(back.event_id, ev.event_id);
|
||||||
teardown_session(&sid);
|
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 image_meta;
|
||||||
pub mod markdown;
|
pub mod markdown;
|
||||||
pub mod material_type;
|
pub mod material_type;
|
||||||
|
pub mod office;
|
||||||
pub mod pdf;
|
pub mod pdf;
|
||||||
pub mod progress;
|
pub mod progress;
|
||||||
pub mod reading_material;
|
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::List { .. })));
|
||||||
assert!(blocks.iter().any(|b| matches!(b, DocumentBlock::CodeBlock { .. })));
|
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).
|
use std::fs;
|
||||||
// Rust Core only provides the unified ReadingPosition model (see progress.rs).
|
use std::path::Path;
|
||||||
// Full PDF parsing (pdfium, text extraction) is deferred to a later milestone.
|
|
||||||
|
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);
|
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 struct SearchResult {
|
||||||
pub block_id: String,
|
pub block_id: String,
|
||||||
pub line_number: Option<u32>,
|
pub line_number: Option<u32>,
|
||||||
|
pub page_number: Option<u32>,
|
||||||
|
pub chapter_id: Option<String>,
|
||||||
pub snippet: String,
|
pub snippet: String,
|
||||||
pub match_start: u64,
|
pub match_start: u64,
|
||||||
pub match_end: 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).
|
/// Search within a list of DocumentBlock (Markdown).
|
||||||
pub fn search_blocks(blocks: &[DocumentBlock], query: &str) -> Vec<SearchResult> {
|
pub fn search_blocks(blocks: &[DocumentBlock], query: &str) -> Vec<SearchResult> {
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
let query = query.to_lowercase();
|
let query_lower = query.to_lowercase();
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
@ -26,33 +63,18 @@ pub fn search_blocks(blocks: &[DocumentBlock], query: &str) -> Vec<SearchResult>
|
|||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let lower = text.to_lowercase();
|
let bid = block_id.to_string();
|
||||||
let mut start = 0;
|
for_each_match_in(&text, &query_lower, |snippet, m_start, m_end, _offset| {
|
||||||
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()
|
|
||||||
};
|
|
||||||
|
|
||||||
results.push(SearchResult {
|
results.push(SearchResult {
|
||||||
block_id: block_id.to_string(),
|
block_id: bid.clone(),
|
||||||
line_number: None,
|
line_number: None,
|
||||||
|
page_number: None,
|
||||||
|
chapter_id: None,
|
||||||
snippet,
|
snippet,
|
||||||
match_start: (abs_start - snippet_start) as u64,
|
match_start: m_start,
|
||||||
match_end: (abs_end - snippet_start) as u64,
|
match_end: m_end,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
start = abs_end;
|
|
||||||
if start >= lower.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results
|
results
|
||||||
@ -63,38 +85,72 @@ pub fn search_text(content: &str, query: &str) -> Vec<SearchResult> {
|
|||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
let query = query.to_lowercase();
|
let query_lower = query.to_lowercase();
|
||||||
let lower = content.to_lowercase();
|
|
||||||
let mut results = Vec::new();
|
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 {
|
results.push(SearchResult {
|
||||||
block_id: format!("line-{line_number}"),
|
block_id: format!("line-{line_number}"),
|
||||||
line_number: Some(line_number),
|
line_number: Some(line_number),
|
||||||
|
page_number: None,
|
||||||
|
chapter_id: None,
|
||||||
snippet,
|
snippet,
|
||||||
match_start: (abs_start - snippet_start) as u64,
|
match_start: m_start,
|
||||||
match_end: (abs_end - snippet_start) as u64,
|
match_end: m_end,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
start = abs_end;
|
results
|
||||||
if start >= lower.len() {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
results
|
||||||
@ -187,4 +243,147 @@ mod tests {
|
|||||||
let results = search_text(content, "");
|
let results = search_text(content, "");
|
||||||
assert!(results.is_empty());
|
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.
|
/// Remove a closed session from memory. Refuses to remove Active/Paused sessions.
|
||||||
pub fn remove_session_v2(session_id: &str) -> Result<(), SessionError> {
|
pub fn remove_session_v2(session_id: &str) -> Result<(), SessionError> {
|
||||||
match (sessions().lock(), trackers().lock()) {
|
match (sessions().lock(), trackers().lock()) {
|
||||||
@ -309,4 +334,92 @@ mod tests {
|
|||||||
teardown(&a);
|
teardown(&a);
|
||||||
teardown(&b);
|
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::events::ReadingEvent;
|
||||||
pub use zx_document_core::reading_material::ReadingMaterialRef;
|
pub use zx_document_core::reading_material::ReadingMaterialRef;
|
||||||
pub use zx_document_core::events_v2::{ReadingEventV2, ReadingEventTypeV2};
|
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};
|
pub use zx_document_core::session_v2::{ReadingSessionV2, ReadingSessionStatus};
|
||||||
|
|
||||||
use zx_document_core::blocks as core_blocks;
|
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)
|
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]
|
#[uniffi::export]
|
||||||
fn ack_events_v2(event_ids: Vec<String>) -> u32 {
|
fn ack_events_v2(event_ids: Vec<String>) -> u32 {
|
||||||
zx_document_core::events_v2::ack_events_v2(&event_ids)
|
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)
|
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]
|
#[uniffi::export]
|
||||||
fn create_note_anchor(material_id: String, position: Option<ReadingPosition>) -> NoteAnchor {
|
fn create_note_anchor(material_id: String, position: Option<ReadingPosition>) -> NoteAnchor {
|
||||||
zx_document_core::anchors::NoteAnchor::from_position(&material_id, position.as_ref())
|
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]
|
#[uniffi::export]
|
||||||
fn push_reading_event(event: ReadingEvent) {
|
fn push_reading_event(event: ReadingEvent) {
|
||||||
zx_document_core::events::push_reading_event(event)
|
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);
|
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.
|
// Reverse conversion: FFI DocumentBlock → core DocumentBlock, used by search.
|
||||||
fn core_block_from_ffi(block: DocumentBlock) -> core_blocks::DocumentBlock {
|
fn core_block_from_ffi(block: DocumentBlock) -> core_blocks::DocumentBlock {
|
||||||
match block {
|
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_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([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 push_reading_event(ReadingEvent event);
|
||||||
void update_reading_position([ByRef] string material_id, ReadingPosition position);
|
void update_reading_position([ByRef] string material_id, ReadingPosition position);
|
||||||
sequence<ReadingEvent> export_pending_events();
|
sequence<ReadingEvent> export_pending_events();
|
||||||
@ -35,6 +101,50 @@ enum DocumentError {
|
|||||||
"IoError",
|
"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]
|
[Enum]
|
||||||
interface MaterialType {
|
interface MaterialType {
|
||||||
Markdown();
|
Markdown();
|
||||||
@ -88,13 +198,14 @@ interface ReadingEvent {
|
|||||||
|
|
||||||
[Enum]
|
[Enum]
|
||||||
interface NoteAnchor {
|
interface NoteAnchor {
|
||||||
Material(string material_id);
|
Material(string material_id, ReadingPosition? position_snapshot);
|
||||||
MarkdownBlock(string material_id, string block_id);
|
MarkdownBlock(string material_id, string block_id, ReadingPosition? position_snapshot);
|
||||||
TextLine(string material_id, u32 line_number);
|
TextLine(string material_id, u32 line_number, ReadingPosition? position_snapshot);
|
||||||
PdfPage(string material_id, u32 page_number);
|
PdfPage(string material_id, u32 page_number, ReadingPosition? position_snapshot);
|
||||||
Image(string material_id);
|
Image(string material_id, ReadingPosition? position_snapshot);
|
||||||
EpubChapter(string material_id, string chapter_id);
|
EpubChapter(string material_id, string chapter_id, ReadingPosition? position_snapshot);
|
||||||
KnowledgeItem(string knowledge_item_id);
|
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 {
|
dictionary ImageMeta {
|
||||||
@ -107,11 +218,54 @@ dictionary ImageMeta {
|
|||||||
dictionary SearchResult {
|
dictionary SearchResult {
|
||||||
string block_id;
|
string block_id;
|
||||||
u32? line_number;
|
u32? line_number;
|
||||||
|
u32? page_number;
|
||||||
|
string? chapter_id;
|
||||||
string snippet;
|
string snippet;
|
||||||
u64 match_start;
|
u64 match_start;
|
||||||
u64 match_end;
|
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 {
|
dictionary TextStats {
|
||||||
u32 line_count;
|
u32 line_count;
|
||||||
u32 word_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
|
```swift
|
||||||
// Rust 生成事件 → Swift 缓存 + 上传
|
// 1. 创建 ReadingMaterialRef(Rust 不存储 readingTargetType,iOS 上传时补充)
|
||||||
class ReadingEventCollector {
|
let material = ReadingMaterialRef(materialId: "mat_001")
|
||||||
private var buffer: [ReadingEvent] = []
|
|
||||||
|
|
||||||
func collect() {
|
// 2. 启动阅读会话(返回 clientSessionId)
|
||||||
// Rust 侧事件暂存
|
let timestamp = Int64(Date().timeIntervalSince1970 * 1000)
|
||||||
|
let sessionId = try startReadingSessionV2(material: material, timestampMs: timestamp)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 推送 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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func flush() {
|
// 3. 上传到 API
|
||||||
// POST buffer → /reading/events
|
api.uploadEvents(uploadItems) { result in
|
||||||
// clear buffer
|
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 的项目示例
|
## 6. 使用真实 XCFramework 的项目示例
|
||||||
|
|
||||||
见 `bindings/ios/demo/` 目录,包含最小 SwiftUI App 示例:
|
见 `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 / 系统预览 |
|
| Excel | `.xls` `.xlsx` | PlatformPreview | 不解析 | QuickLook / 系统预览 |
|
||||||
| PPT | `.ppt` `.pptx` | ExternalOpen | 不解析 | 外部 App 打开 |
|
| 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 | 备注 |
|
| 格式 | 目标 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