diff --git a/AIStudyApp/AIStudyApp.xcodeproj/project.pbxproj b/AIStudyApp/AIStudyApp.xcodeproj/project.pbxproj index 0cf9c78..ffeb624 100644 --- a/AIStudyApp/AIStudyApp.xcodeproj/project.pbxproj +++ b/AIStudyApp/AIStudyApp.xcodeproj/project.pbxproj @@ -129,6 +129,8 @@ 05F6CD242FA886350043A7BC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/AIStudyApp/BridgingHeader.h"; + ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; @@ -191,6 +193,8 @@ 05F6CD252FA886350043A7BC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/AIStudyApp/BridgingHeader.h"; + ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; @@ -245,6 +249,8 @@ 05F6CD272FA886350043A7BC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/AIStudyApp/BridgingHeader.h"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = AIStudyApp.entitlements; @@ -295,6 +301,8 @@ 05F6CD282FA886350043A7BC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/AIStudyApp/BridgingHeader.h"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = AIStudyApp.entitlements; diff --git a/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate b/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate index 55c85cb..76c5fae 100644 Binary files a/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate and b/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/AIStudyApp/AIStudyApp/AIStudyAppApp.swift b/AIStudyApp/AIStudyApp/AIStudyAppApp.swift index df7ab50..e627ea4 100644 --- a/AIStudyApp/AIStudyApp/AIStudyAppApp.swift +++ b/AIStudyApp/AIStudyApp/AIStudyAppApp.swift @@ -6,6 +6,7 @@ struct AIStudyAppApp: App { @AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = false @AppStorage("appAppearance") private var appAppearance = "system" @StateObject private var authManager = AuthManager() + @Environment(\.scenePhase) private var scenePhase private var effectiveColorScheme: ColorScheme? { switch appAppearance { @@ -60,6 +61,31 @@ struct AIStudyAppApp: App { .task { await authManager.restoreSession() } + .onChange(of: scenePhase) { _, newPhase in + switch newPhase { + case .background: + // Pause active session + stop heartbeat + if ReadingRuntimeSessionManager.shared.state == .active { + ReadingRuntimeSessionManager.shared.pause() + } + // Flush events: export → enqueue → (quick upload in background task) + Task { + let pipeline = ReadingEventUploadPipeline.shared + pipeline.exportAndEnqueue(contexts: [:]) // contexts populated from active sessions + await pipeline.flush() + } + case .active: + // Resume session + reload stale events from Rust + ReadingRuntimeSessionManager.shared.resume() + Task { + ReadingEventUploadPipeline.shared.reloadOnLaunch(contexts: [:]) + } + case .inactive: + break + @unknown default: + break + } + } } } } diff --git a/AIStudyApp/AIStudyApp/BridgingHeader.h b/AIStudyApp/AIStudyApp/BridgingHeader.h new file mode 100644 index 0000000..3657e04 --- /dev/null +++ b/AIStudyApp/AIStudyApp/BridgingHeader.h @@ -0,0 +1 @@ +#import "Core/Services/zx_documentFFI.h" diff --git a/AIStudyApp/AIStudyApp/Core/Models/ReadingEventUploadItem.swift b/AIStudyApp/AIStudyApp/Core/Models/ReadingEventUploadItem.swift new file mode 100644 index 0000000..c1b507a --- /dev/null +++ b/AIStudyApp/AIStudyApp/Core/Models/ReadingEventUploadItem.swift @@ -0,0 +1,128 @@ +import Foundation + +// MARK: - Upload Item + +struct ReadingEventUploadItem: Equatable { + let eventId: String + let clientSessionId: String + let materialId: String + let eventType: String + let position: ReadingPosition? + let activeSecondsDelta: Int + let clientTimestampMs: Int64 + let sequence: UInt64 + + let readingTargetType: String + let platform: String + let appVersion: String + let clientTimezoneOffsetMinutes: Int +} + +// MARK: - Codable (manual — ReadingPosition is UniFFI, not Codable) + +extension ReadingEventUploadItem: Codable { + enum CodingKeys: String, CodingKey { + case eventId, clientSessionId, materialId, eventType + case activeSecondsDelta, clientTimestampMs, sequence + case readingTargetType, platform, appVersion, clientTimezoneOffsetMinutes + case position + } + + init(from decoder: Decoder) throws { + let c = try decoder.container(keyedBy: CodingKeys.self) + eventId = try c.decode(String.self, forKey: .eventId) + clientSessionId = try c.decode(String.self, forKey: .clientSessionId) + materialId = try c.decode(String.self, forKey: .materialId) + eventType = try c.decode(String.self, forKey: .eventType) + activeSecondsDelta = try c.decode(Int.self, forKey: .activeSecondsDelta) + clientTimestampMs = try c.decode(Int64.self, forKey: .clientTimestampMs) + sequence = try c.decode(UInt64.self, forKey: .sequence) + readingTargetType = try c.decode(String.self, forKey: .readingTargetType) + platform = try c.decode(String.self, forKey: .platform) + appVersion = try c.decode(String.self, forKey: .appVersion) + clientTimezoneOffsetMinutes = try c.decode(Int.self, forKey: .clientTimezoneOffsetMinutes) + position = nil // Won't decode from API response — only sent, never received + } + + func encode(to encoder: Encoder) throws { + var c = encoder.container(keyedBy: CodingKeys.self) + try c.encode(eventId, forKey: .eventId) + try c.encode(clientSessionId, forKey: .clientSessionId) + try c.encode(materialId, forKey: .materialId) + try c.encode(eventType, forKey: .eventType) + try c.encode(activeSecondsDelta, forKey: .activeSecondsDelta) + try c.encode(clientTimestampMs, forKey: .clientTimestampMs) + try c.encode(sequence, forKey: .sequence) + try c.encode(readingTargetType, forKey: .readingTargetType) + try c.encode(platform, forKey: .platform) + try c.encode(appVersion, forKey: .appVersion) + try c.encode(clientTimezoneOffsetMinutes, forKey: .clientTimezoneOffsetMinutes) + // Encode position as nested JSON object + if let pos = position { + try c.encode(PositionWrapper(from: pos), forKey: .position) + } + } +} + +/// Codable wrapper for ReadingPosition (UniFFI enum without Codable). +private struct PositionWrapper: Encodable { + let raw: [String: AnyCodableValue] + + init(from pos: ReadingPosition) { + switch pos { + case .markdown(let blockId, let scrollProgress): + raw = ["type": .string("Markdown"), "blockId": .string(blockId), "scrollProgress": .float(scrollProgress)] + case .text(let lineNumber, let scrollProgress): + raw = ["type": .string("Text"), "lineNumber": .int(Int(lineNumber)), "scrollProgress": .float(scrollProgress)] + case .pdf(let pageNumber, let pageProgress, let overallProgress): + raw = ["type": .string("Pdf"), "pageNumber": .int(Int(pageNumber)), "pageProgress": .float(pageProgress), "overallProgress": .float(overallProgress)] + case .image(let zoomScale, let offsetX, let offsetY): + raw = ["type": .string("Image"), "zoomScale": .float(zoomScale), "offsetX": .float(offsetX), "offsetY": .float(offsetY)] + case .epub(let chapterId, let chapterProgress, let overallProgress): + raw = ["type": .string("Epub"), "chapterId": .string(chapterId), "chapterProgress": .float(chapterProgress), "overallProgress": .float(overallProgress)] + case .unknown: + raw = ["type": .string("Unknown")] + } + } + + func encode(to encoder: Encoder) throws { + var c = encoder.container(keyedBy: DynamicCodingKey.self) + for (key, value) in raw { + let codingKey = DynamicCodingKey(stringValue: key) + switch value { + case .string(let s): try c.encode(s, forKey: codingKey) + case .int(let i): try c.encode(i, forKey: codingKey) + case .float(let f): try c.encode(f, forKey: codingKey) + } + } + } +} + +private enum AnyCodableValue { + case string(String), int(Int), float(Float) +} + +private struct DynamicCodingKey: CodingKey { + let stringValue: String; let intValue: Int? = nil + init(stringValue: String) { self.stringValue = stringValue } + init?(intValue: Int) { nil } +} + +// MARK: - Batch Request / Response + +struct ReadingEventBatchRequest: Codable { + let events: [ReadingEventUploadItem] +} + +struct ReadingEventBatchResponse: Codable { + let processed: Int + let duplicate: Int + let failed: Int + let warnings: [WarningItem]? + + struct WarningItem: Codable { + let eventId: String? + let code: String + let message: String + } +} diff --git a/AIStudyApp/AIStudyApp/Core/Models/ReadingMaterialContext.swift b/AIStudyApp/AIStudyApp/Core/Models/ReadingMaterialContext.swift new file mode 100644 index 0000000..6a0a210 --- /dev/null +++ b/AIStudyApp/AIStudyApp/Core/Models/ReadingMaterialContext.swift @@ -0,0 +1,31 @@ +import Foundation + +// MARK: - ReadingTargetType + +enum ReadingTargetType: String, Codable, Equatable { + case knowledgeSource = "knowledge_source" + case temporaryFile = "temporary_file" +} + +// MARK: - ReadingMaterialContext + +/// iOS-side context that supplements Rust's materialId with business fields. +/// Rust only knows materialId; iOS adds readingTargetType/knowledgeBaseId for API upload. +struct ReadingMaterialContext: Equatable { + let readingTargetType: ReadingTargetType + let materialId: String + let knowledgeBaseId: String? + let title: String? + + init( + readingTargetType: ReadingTargetType, + materialId: String, + knowledgeBaseId: String? = nil, + title: String? = nil + ) { + self.readingTargetType = readingTargetType + self.materialId = materialId + self.knowledgeBaseId = knowledgeBaseId + self.title = title + } +} diff --git a/AIStudyApp/AIStudyApp/Core/Services/ReadingAPI.swift b/AIStudyApp/AIStudyApp/Core/Services/ReadingAPI.swift new file mode 100644 index 0000000..231b717 --- /dev/null +++ b/AIStudyApp/AIStudyApp/Core/Services/ReadingAPI.swift @@ -0,0 +1,113 @@ +import Foundation + +// MARK: - Reading API Service + +@MainActor +class ReadingAPIService { + static let shared = ReadingAPIService() + private let client = APIClient.shared + + /// POST /learning/reading-events/batch + func uploadReadingEvents(_ events: [ReadingEventUploadItem]) async throws -> ReadingEventBatchResponse { + let body = ReadingEventBatchRequest(events: events) + return try await client.request("/learning/reading-events/batch", method: "POST", body: body) + } + + /// GET /materials/:id/reading-progress + func getReadingProgress(materialId: String, targetType: String) async throws -> MaterialReadingProgressDTO { + return try await client.request( + "/materials/\(materialId)/reading-progress", + queryItems: [URLQueryItem(name: "readingTargetType", value: targetType)] + ) + } + + /// GET /learning/continue + func getContinueLearning() async throws -> ContinueLearningResponse { + return try await client.request("/learning/continue") + } + + /// GET /learning/summary + func getLearningSummary() async throws -> LearningSummaryResponse { + return try await client.request("/learning/summary") + } + + /// GET /learning/trend?days=7 + func getLearningTrend(days: Int = 7) async throws -> LearningTrendResponse { + return try await client.request( + "/learning/trend", + queryItems: [URLQueryItem(name: "days", value: String(days))] + ) + } + + /// GET /learning/records + func getLearningRecords(cursor: String? = nil, limit: Int = 20, type: String? = nil) async throws -> LearningRecordsResponse { + var items = [URLQueryItem(name: "limit", value: String(limit))] + if let c = cursor { items.append(URLQueryItem(name: "cursor", value: c)) } + if let t = type { items.append(URLQueryItem(name: "type", value: t)) } + return try await client.request("/learning/records", queryItems: items) + } +} + +// MARK: - Response DTOs + +struct MaterialReadingProgressDTO: Codable { + let status: String + let lastProgress: Float? + let totalActiveSeconds: Int + let isMarkedRead: Bool + let firstOpenedAt: String? + let lastReadAt: String? +} + +struct ContinueLearningResponse: Codable { + let type: String? + let materialId: String? + let title: String? + let lastProgress: Float? + let totalActiveSeconds: Int? + let lastReadAt: String? +} + +struct LearningSummaryResponse: Codable { + let todaySeconds: Int + let weekSeconds: Int + let totalSeconds: Int + let activeDays: Int + let sessionsCount: Int + let materialsReadCount: Int + let markedReadCount: Int + let dailyAverageSeconds: Int +} + +struct LearningTrendResponse: Codable { + let days: Int + let series: [TrendPoint] + + struct TrendPoint: Codable { + let date: String + let value: Int + } +} + +struct LearningRecordsResponse: Codable { + let items: [RecordItem] + let nextCursor: String? + + struct RecordItem: Codable { + let id: String + let recordType: String + let title: String + let description: String? + let durationSeconds: Int + let occurredAt: String + let metadata: RecordMetadata? + let createdAt: String + } + + struct RecordMetadata: Codable { + let materialId: String? + let readingTargetType: String? + let knowledgeBaseId: String? + let totalActiveSeconds: Int? + } +} diff --git a/AIStudyApp/AIStudyApp/Core/Services/ReadingRuntimeAdapter.swift b/AIStudyApp/AIStudyApp/Core/Services/ReadingRuntimeAdapter.swift new file mode 100644 index 0000000..00120ce --- /dev/null +++ b/AIStudyApp/AIStudyApp/Core/Services/ReadingRuntimeAdapter.swift @@ -0,0 +1,100 @@ +import Foundation + +// MARK: - Runtime Session Wrapper + +/// Holds the Rust client_session_id for the active reading session. +struct ReadingRuntimeSession { + let sessionId: String + let materialContext: ReadingMaterialContext + let startedAt: Date +} + +// MARK: - ReadingRuntimeAdapter Protocol + +/// Abstracts all Rust zx_document FFI calls. +/// The default implementation wraps ZxDocumentRuntime (uniffi-generated). +protocol ReadingRuntimeAdapter { + // ── Session Lifecycle ── + + func startSession(material: ReadingMaterialRef, timestampMs: Int64) throws -> String + func pauseSession(_ sessionId: String) throws + func resumeSession(_ sessionId: String) throws + func closeSession(_ sessionId: String) throws + + // ── Event Push ── + + func pushOpened(sessionId: String, materialId: String, timestampMs: Int64) throws -> ReadingEventV2 + func pushClosed(sessionId: String, materialId: String, delta: UInt32, timestampMs: Int64) throws -> ReadingEventV2 + func pushPositionChanged(sessionId: String, materialId: String, position: ReadingPosition, timestampMs: Int64) throws -> ReadingEventV2 + func pushHeartbeat(sessionId: String, materialId: String, delta: UInt32, position: ReadingPosition?, timestampMs: Int64) throws -> ReadingEventV2 + func pushMarkedAsRead(sessionId: String, materialId: String, timestampMs: Int64) throws -> ReadingEventV2 + + // ── Buffer Management ── + + func exportEvents(limit: UInt32, timestampMs: Int64) -> [ReadingEventV2] + func ackEvents(_ eventIds: [String]) -> UInt32 + func markFailed(_ eventIds: [String]) -> UInt32 + func reloadStaleEvents() -> UInt32 + func cleanupStaleSessions(nowMs: Int64, maxAgeMs: Int64) -> UInt32 +} + +// MARK: - Default Implementation (Rust FFI) + +final class RustReadingRuntimeAdapter: ReadingRuntimeAdapter { + func startSession(material: ReadingMaterialRef, timestampMs: Int64) throws -> String { + let result = try startReadingSessionV2(material: material, timestampMs: timestampMs) + return result + } + + func pauseSession(_ sessionId: String) throws { + try pauseReadingSessionV2(sessionId: sessionId) + } + + func resumeSession(_ sessionId: String) throws { + try resumeReadingSessionV2(sessionId: sessionId) + } + + func closeSession(_ sessionId: String) throws { + try closeReadingSessionV2(sessionId: sessionId) + } + + func pushOpened(sessionId: String, materialId: String, timestampMs: Int64) throws -> ReadingEventV2 { + return try pushMaterialOpenedV2(sessionId: sessionId, materialId: materialId, timestampMs: timestampMs) + } + + func pushClosed(sessionId: String, materialId: String, delta: UInt32, timestampMs: Int64) throws -> ReadingEventV2 { + return try pushMaterialClosedV2(sessionId: sessionId, materialId: materialId, activeSecondsDelta: delta, timestampMs: timestampMs) + } + + func pushPositionChanged(sessionId: String, materialId: String, position: ReadingPosition, timestampMs: Int64) throws -> ReadingEventV2 { + return try pushPositionChangedV2(sessionId: sessionId, materialId: materialId, position: position, timestampMs: timestampMs) + } + + func pushHeartbeat(sessionId: String, materialId: String, delta: UInt32, position: ReadingPosition?, timestampMs: Int64) throws -> ReadingEventV2 { + return try pushHeartbeatV2(sessionId: sessionId, materialId: materialId, activeSecondsDelta: delta, position: position, timestampMs: timestampMs) + } + + func pushMarkedAsRead(sessionId: String, materialId: String, timestampMs: Int64) throws -> ReadingEventV2 { + return try pushMarkedAsReadV2(sessionId: sessionId, materialId: materialId, timestampMs: timestampMs) + } + + func exportEvents(limit: UInt32, timestampMs: Int64) -> [ReadingEventV2] { + return exportPendingEventsV2(limit: limit, timestampMs: timestampMs) + } + + func ackEvents(_ eventIds: [String]) -> UInt32 { + return ackEventsV2(eventIds: eventIds) + } + + func markFailed(_ eventIds: [String]) -> UInt32 { + return markEventsFailedV2(eventIds: eventIds) + } + + func reloadStaleEvents() -> UInt32 { + return reloadStaleEventsV2() + } + + func cleanupStaleSessions(nowMs: Int64, maxAgeMs: Int64) -> UInt32 { + return cleanupStaleSessionsFfi(nowMs: nowMs, maxAgeMs: maxAgeMs) + } +} diff --git a/AIStudyApp/AIStudyApp/Core/Services/zx_document.swift b/AIStudyApp/AIStudyApp/Core/Services/zx_document.swift index 739298f..7774da8 100644 --- a/AIStudyApp/AIStudyApp/Core/Services/zx_document.swift +++ b/AIStudyApp/AIStudyApp/Core/Services/zx_document.swift @@ -4,62 +4,7 @@ // swiftlint:disable all import Foundation -// Inline FFI types (matching C ABI exactly) -public struct RustBuffer { - public var capacity: UInt64 - public var len: UInt64 - public var data: UnsafeMutableRawPointer? - public init() { self.capacity = 0; self.len = 0; self.data = nil } - public init(capacity: UInt64, len: UInt64, data: UnsafeMutableRawPointer?) { - self.capacity = capacity; self.len = len; self.data = data - } -} -public struct ForeignBytes { - public var len: Int32 - public var data: UnsafeRawPointer? -} -public struct RustCallStatus { - public var code: Int8 - public var errorBuf: RustBuffer - public init(code: Int8, errorBuf: RustBuffer) { self.code = code; self.errorBuf = errorBuf } -} - -#if canImport(zx_documentFFI) -import zx_documentFFI -#endif - -@_silgen_name("ffi_zx_document_ffi_rustbuffer_alloc") private func ffi_zx_document_ffi_rustbuffer_alloc(_ size: UInt64, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("ffi_zx_document_ffi_rustbuffer_from_bytes") private func ffi_zx_document_ffi_rustbuffer_from_bytes(_ bytes: ForeignBytes, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("ffi_zx_document_ffi_rustbuffer_free") private func ffi_zx_document_ffi_rustbuffer_free(_ buf: RustBuffer, _ status: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_rustbuffer_reserve") private func ffi_zx_document_ffi_rustbuffer_reserve(_ buf: RustBuffer, _ additional: UInt64, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("ffi_zx_document_ffi_uniffi_contract_version") private func ffi_zx_document_ffi_uniffi_contract_version() -> Int32 -// Out-pointer functions (avoid struct-passing ABI issues on ARM64 iOS) -@_silgen_name("ffi_zx_document_ffi_rustbuffer_from_bytes_separate") private func ffi_zx_document_ffi_rustbuffer_from_bytes_separate(_ len: Int32, _ data: UnsafeRawPointer, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_parse_markdown_separate") private func ffi_zx_document_ffi_parse_markdown_separate(_ len: Int32, _ data: UnsafeRawPointer, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_rustbuffer_free_separate") private func ffi_zx_document_ffi_rustbuffer_free_separate(_ capacity: UInt64, _ len: UInt64, _ data: UnsafeMutableRawPointer?) -@_silgen_name("ffi_zx_document_ffi_detect_material_type_separate") private func ffi_zx_document_ffi_detect_material_type_separate(_ len: Int32, _ data: UnsafeRawPointer, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_read_image_meta_separate") private func ffi_zx_document_ffi_read_image_meta_separate(_ len: Int32, _ data: UnsafeRawPointer, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_read_text_stats_separate") private func ffi_zx_document_ffi_read_text_stats_separate(_ len: Int32, _ data: UnsafeRawPointer, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_parse_text_separate") private func ffi_zx_document_ffi_parse_text_separate(_ len: Int32, _ data: UnsafeRawPointer, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_push_reading_event_separate") private func ffi_zx_document_ffi_push_reading_event_separate(_ event_cap: UInt64, _ event_len: UInt64, _ event_data: UnsafeRawPointer) -@_silgen_name("ffi_zx_document_ffi_update_reading_position_separate") private func ffi_zx_document_ffi_update_reading_position_separate(_ mid_len: Int32, _ mid_data: UnsafeRawPointer, _ pos_cap: UInt64, _ pos_len: UInt64, _ pos_data: UnsafeRawPointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_export_pending_events_separate") private func ffi_zx_document_ffi_export_pending_events_separate(_ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_create_note_anchor_separate") private func ffi_zx_document_ffi_create_note_anchor_separate(_ mid_len: Int32, _ mid_data: UnsafeRawPointer, _ pos_cap: UInt64, _ pos_len: UInt64, _ pos_data: UnsafeRawPointer, _ pos_has_value: Int8, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_search_markdown_blocks_separate") private func ffi_zx_document_ffi_search_markdown_blocks_separate(_ blocks_cap: UInt64, _ blocks_len: UInt64, _ blocks_data: UnsafeRawPointer, _ query_len: Int32, _ query_data: UnsafeRawPointer, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -@_silgen_name("ffi_zx_document_ffi_search_text_content_separate") private func ffi_zx_document_ffi_search_text_content_separate(_ content_len: Int32, _ content_data: UnsafeRawPointer, _ query_len: Int32, _ query_data: UnsafeRawPointer, _ out_capacity: UnsafeMutablePointer, _ out_len: UnsafeMutablePointer, _ out_data: UnsafeMutablePointer, _ out_error_code: UnsafeMutablePointer) -// Original UniFFI functions (kept for reference) -@_silgen_name("uniffi_zx_document_ffi_fn_func_detect_material_type") private func uniffi_zx_document_ffi_fn_func_detect_material_type(_ filePath: RustBuffer, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_parse_markdown") private func uniffi_zx_document_ffi_fn_func_parse_markdown(_ content: RustBuffer, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_parse_text") private func uniffi_zx_document_ffi_fn_func_parse_text(_ content: RustBuffer, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_read_image_meta") private func uniffi_zx_document_ffi_fn_func_read_image_meta(_ filePath: RustBuffer, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_read_text_stats") private func uniffi_zx_document_ffi_fn_func_read_text_stats(_ filePath: RustBuffer, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_search_markdown_blocks") private func uniffi_zx_document_ffi_fn_func_search_markdown_blocks(_ blocks: RustBuffer, _ query: RustBuffer, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_search_text_content") private func uniffi_zx_document_ffi_fn_func_search_text_content(_ content: RustBuffer, _ query: RustBuffer, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_create_note_anchor") private func uniffi_zx_document_ffi_fn_func_create_note_anchor(_ materialId: RustBuffer, _ position: RustBuffer, _ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_push_reading_event") private func uniffi_zx_document_ffi_fn_func_push_reading_event(_ event: RustBuffer, _ status: UnsafeMutablePointer) -@_silgen_name("uniffi_zx_document_ffi_fn_func_update_reading_position") private func uniffi_zx_document_ffi_fn_func_update_reading_position(_ materialId: RustBuffer, _ position: RustBuffer, _ status: UnsafeMutablePointer) -@_silgen_name("uniffi_zx_document_ffi_fn_func_export_pending_events") private func uniffi_zx_document_ffi_fn_func_export_pending_events(_ status: UnsafeMutablePointer) -> RustBuffer -@_silgen_name("uniffi_zx_document_ffi_fn_func_clear_exported_events") private func uniffi_zx_document_ffi_fn_func_clear_exported_events(_ count: UInt32, _ status: UnsafeMutablePointer) +// C types imported via BridgingHeader.h private func uniffiEnsureZxDocumentFfiInitialized() {} @@ -77,24 +22,17 @@ fileprivate extension RustBuffer { } static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer { - var capacity: UInt64 = 0 - var len: UInt64 = 0 - var data: UnsafeMutableRawPointer? = nil - ffi_zx_document_ffi_rustbuffer_from_bytes_separate( - Int32(ptr.count), UnsafeRawPointer(ptr.baseAddress!), - &capacity, &len, &data - ) - return RustBuffer(capacity: capacity, len: len, data: data) + try! rustCall { ffi_zx_document_ffi_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } } // Frees the buffer in place. // The buffer must not be used after this is called. func deallocate() { - ffi_zx_document_ffi_rustbuffer_free_separate(self.capacity, self.len, self.data) + try! rustCall { ffi_zx_document_ffi_rustbuffer_free(self, $0) } } } -fileprivate extension ForeignBytes { +extension ForeignBytes { init(bufferPointer: UnsafeBufferPointer) { self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress) } @@ -309,7 +247,7 @@ fileprivate let CALL_ERROR: Int8 = 1 fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2 fileprivate let CALL_CANCELLED: Int8 = 3 -fileprivate extension RustCallStatus { +extension RustCallStatus { init() { self.init( code: CALL_SUCCESS, @@ -590,7 +528,7 @@ fileprivate struct FfiConverterString: FfiConverter { if value.data == nil { return String() } - let bytes = UnsafeBufferPointer(start: value.data!.assumingMemoryBound(to: UInt8.self), count: Int(value.len)) + let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len)) return String(bytes: bytes, encoding: String.Encoding.utf8)! } @@ -696,6 +634,130 @@ public func FfiConverterTypeDocumentInfo_lower(_ value: DocumentInfo) -> RustBuf } +public struct EpubChapter: Equatable, Hashable { + public var chapterId: String + public var title: String + public var path: String + public var playOrder: UInt32 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(chapterId: String, title: String, path: String, playOrder: UInt32) { + self.chapterId = chapterId + self.title = title + self.path = path + self.playOrder = playOrder + } + + + + +} + +#if compiler(>=6) +extension EpubChapter: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeEpubChapter: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> EpubChapter { + return + try EpubChapter( + chapterId: FfiConverterString.read(from: &buf), + title: FfiConverterString.read(from: &buf), + path: FfiConverterString.read(from: &buf), + playOrder: FfiConverterUInt32.read(from: &buf) + ) + } + + public static func write(_ value: EpubChapter, into buf: inout [UInt8]) { + FfiConverterString.write(value.chapterId, into: &buf) + FfiConverterString.write(value.title, into: &buf) + FfiConverterString.write(value.path, into: &buf) + FfiConverterUInt32.write(value.playOrder, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeEpubChapter_lift(_ buf: RustBuffer) throws -> EpubChapter { + return try FfiConverterTypeEpubChapter.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeEpubChapter_lower(_ value: EpubChapter) -> RustBuffer { + return FfiConverterTypeEpubChapter.lower(value) +} + + +public struct EpubMetadata: Equatable, Hashable { + public var title: String? + public var author: String? + public var chapterCount: UInt32 + public var fileSize: UInt64 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(title: String?, author: String?, chapterCount: UInt32, fileSize: UInt64) { + self.title = title + self.author = author + self.chapterCount = chapterCount + self.fileSize = fileSize + } + + + + +} + +#if compiler(>=6) +extension EpubMetadata: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeEpubMetadata: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> EpubMetadata { + return + try EpubMetadata( + title: FfiConverterOptionString.read(from: &buf), + author: FfiConverterOptionString.read(from: &buf), + chapterCount: FfiConverterUInt32.read(from: &buf), + fileSize: FfiConverterUInt64.read(from: &buf) + ) + } + + public static func write(_ value: EpubMetadata, into buf: inout [UInt8]) { + FfiConverterOptionString.write(value.title, into: &buf) + FfiConverterOptionString.write(value.author, into: &buf) + FfiConverterUInt32.write(value.chapterCount, into: &buf) + FfiConverterUInt64.write(value.fileSize, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeEpubMetadata_lift(_ buf: RustBuffer) throws -> EpubMetadata { + return try FfiConverterTypeEpubMetadata.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeEpubMetadata_lower(_ value: EpubMetadata) -> RustBuffer { + return FfiConverterTypeEpubMetadata.lower(value) +} + + public struct ImageMeta: Equatable, Hashable { public var width: UInt32 public var height: UInt32 @@ -758,18 +820,406 @@ public func FfiConverterTypeImageMeta_lower(_ value: ImageMeta) -> RustBuffer { } +public struct OfficePreviewConfig: Equatable, Hashable { + public var materialType: MaterialType + public var strategy: OfficePreviewStrategy + public var fileSize: UInt64 + public var supportsSearchAfterConversion: Bool + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(materialType: MaterialType, strategy: OfficePreviewStrategy, fileSize: UInt64, supportsSearchAfterConversion: Bool) { + self.materialType = materialType + self.strategy = strategy + self.fileSize = fileSize + self.supportsSearchAfterConversion = supportsSearchAfterConversion + } + + + + +} + +#if compiler(>=6) +extension OfficePreviewConfig: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeOfficePreviewConfig: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OfficePreviewConfig { + return + try OfficePreviewConfig( + materialType: FfiConverterTypeMaterialType.read(from: &buf), + strategy: FfiConverterTypeOfficePreviewStrategy.read(from: &buf), + fileSize: FfiConverterUInt64.read(from: &buf), + supportsSearchAfterConversion: FfiConverterBool.read(from: &buf) + ) + } + + public static func write(_ value: OfficePreviewConfig, into buf: inout [UInt8]) { + FfiConverterTypeMaterialType.write(value.materialType, into: &buf) + FfiConverterTypeOfficePreviewStrategy.write(value.strategy, into: &buf) + FfiConverterUInt64.write(value.fileSize, into: &buf) + FfiConverterBool.write(value.supportsSearchAfterConversion, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeOfficePreviewConfig_lift(_ buf: RustBuffer) throws -> OfficePreviewConfig { + return try FfiConverterTypeOfficePreviewConfig.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeOfficePreviewConfig_lower(_ value: OfficePreviewConfig) -> RustBuffer { + return FfiConverterTypeOfficePreviewConfig.lower(value) +} + + +public struct PdfMetadata: Equatable, Hashable { + public var pageCount: UInt32 + public var title: String? + public var author: String? + public var fileSize: UInt64 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(pageCount: UInt32, title: String?, author: String?, fileSize: UInt64) { + self.pageCount = pageCount + self.title = title + self.author = author + self.fileSize = fileSize + } + + + + +} + +#if compiler(>=6) +extension PdfMetadata: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypePdfMetadata: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PdfMetadata { + return + try PdfMetadata( + pageCount: FfiConverterUInt32.read(from: &buf), + title: FfiConverterOptionString.read(from: &buf), + author: FfiConverterOptionString.read(from: &buf), + fileSize: FfiConverterUInt64.read(from: &buf) + ) + } + + public static func write(_ value: PdfMetadata, into buf: inout [UInt8]) { + FfiConverterUInt32.write(value.pageCount, into: &buf) + FfiConverterOptionString.write(value.title, into: &buf) + FfiConverterOptionString.write(value.author, into: &buf) + FfiConverterUInt64.write(value.fileSize, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypePdfMetadata_lift(_ buf: RustBuffer) throws -> PdfMetadata { + return try FfiConverterTypePdfMetadata.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypePdfMetadata_lower(_ value: PdfMetadata) -> RustBuffer { + return FfiConverterTypePdfMetadata.lower(value) +} + + +public struct PdfPageText: Equatable, Hashable { + public var pageNumber: UInt32 + public var text: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(pageNumber: UInt32, text: String) { + self.pageNumber = pageNumber + self.text = text + } + + + + +} + +#if compiler(>=6) +extension PdfPageText: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypePdfPageText: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PdfPageText { + return + try PdfPageText( + pageNumber: FfiConverterUInt32.read(from: &buf), + text: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: PdfPageText, into buf: inout [UInt8]) { + FfiConverterUInt32.write(value.pageNumber, into: &buf) + FfiConverterString.write(value.text, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypePdfPageText_lift(_ buf: RustBuffer) throws -> PdfPageText { + return try FfiConverterTypePdfPageText.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypePdfPageText_lower(_ value: PdfPageText) -> RustBuffer { + return FfiConverterTypePdfPageText.lower(value) +} + + +public struct ReadingEventV2: Equatable, Hashable { + public var eventId: String + public var clientSessionId: String + public var materialId: String + public var eventType: ReadingEventTypeV2 + public var position: ReadingPosition? + public var activeSecondsDelta: UInt32 + public var timestampMs: Int64 + public var sequence: UInt64 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(eventId: String, clientSessionId: String, materialId: String, eventType: ReadingEventTypeV2, position: ReadingPosition?, activeSecondsDelta: UInt32, timestampMs: Int64, sequence: UInt64) { + self.eventId = eventId + self.clientSessionId = clientSessionId + self.materialId = materialId + self.eventType = eventType + self.position = position + self.activeSecondsDelta = activeSecondsDelta + self.timestampMs = timestampMs + self.sequence = sequence + } + + + + +} + +#if compiler(>=6) +extension ReadingEventV2: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeReadingEventV2: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReadingEventV2 { + return + try ReadingEventV2( + eventId: FfiConverterString.read(from: &buf), + clientSessionId: FfiConverterString.read(from: &buf), + materialId: FfiConverterString.read(from: &buf), + eventType: FfiConverterTypeReadingEventTypeV2.read(from: &buf), + position: FfiConverterOptionTypeReadingPosition.read(from: &buf), + activeSecondsDelta: FfiConverterUInt32.read(from: &buf), + timestampMs: FfiConverterInt64.read(from: &buf), + sequence: FfiConverterUInt64.read(from: &buf) + ) + } + + public static func write(_ value: ReadingEventV2, into buf: inout [UInt8]) { + FfiConverterString.write(value.eventId, into: &buf) + FfiConverterString.write(value.clientSessionId, into: &buf) + FfiConverterString.write(value.materialId, into: &buf) + FfiConverterTypeReadingEventTypeV2.write(value.eventType, into: &buf) + FfiConverterOptionTypeReadingPosition.write(value.position, into: &buf) + FfiConverterUInt32.write(value.activeSecondsDelta, into: &buf) + FfiConverterInt64.write(value.timestampMs, into: &buf) + FfiConverterUInt64.write(value.sequence, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingEventV2_lift(_ buf: RustBuffer) throws -> ReadingEventV2 { + return try FfiConverterTypeReadingEventV2.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingEventV2_lower(_ value: ReadingEventV2) -> RustBuffer { + return FfiConverterTypeReadingEventV2.lower(value) +} + + +public struct ReadingMaterialRef: Equatable, Hashable { + public var materialId: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(materialId: String) { + self.materialId = materialId + } + + + + +} + +#if compiler(>=6) +extension ReadingMaterialRef: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeReadingMaterialRef: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReadingMaterialRef { + return + try ReadingMaterialRef( + materialId: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: ReadingMaterialRef, into buf: inout [UInt8]) { + FfiConverterString.write(value.materialId, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingMaterialRef_lift(_ buf: RustBuffer) throws -> ReadingMaterialRef { + return try FfiConverterTypeReadingMaterialRef.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingMaterialRef_lower(_ value: ReadingMaterialRef) -> RustBuffer { + return FfiConverterTypeReadingMaterialRef.lower(value) +} + + +public struct ReadingSessionV2: Equatable, Hashable { + public var clientSessionId: String + public var material: ReadingMaterialRef + public var startedAtMs: Int64 + public var lastEventAtMs: Int64 + public var nextSequence: UInt64 + public var totalActiveSeconds: UInt32 + public var lastPosition: ReadingPosition? + public var status: ReadingSessionStatus + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(clientSessionId: String, material: ReadingMaterialRef, startedAtMs: Int64, lastEventAtMs: Int64, nextSequence: UInt64, totalActiveSeconds: UInt32, lastPosition: ReadingPosition?, status: ReadingSessionStatus) { + self.clientSessionId = clientSessionId + self.material = material + self.startedAtMs = startedAtMs + self.lastEventAtMs = lastEventAtMs + self.nextSequence = nextSequence + self.totalActiveSeconds = totalActiveSeconds + self.lastPosition = lastPosition + self.status = status + } + + + + +} + +#if compiler(>=6) +extension ReadingSessionV2: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeReadingSessionV2: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReadingSessionV2 { + return + try ReadingSessionV2( + clientSessionId: FfiConverterString.read(from: &buf), + material: FfiConverterTypeReadingMaterialRef.read(from: &buf), + startedAtMs: FfiConverterInt64.read(from: &buf), + lastEventAtMs: FfiConverterInt64.read(from: &buf), + nextSequence: FfiConverterUInt64.read(from: &buf), + totalActiveSeconds: FfiConverterUInt32.read(from: &buf), + lastPosition: FfiConverterOptionTypeReadingPosition.read(from: &buf), + status: FfiConverterTypeReadingSessionStatus.read(from: &buf) + ) + } + + public static func write(_ value: ReadingSessionV2, into buf: inout [UInt8]) { + FfiConverterString.write(value.clientSessionId, into: &buf) + FfiConverterTypeReadingMaterialRef.write(value.material, into: &buf) + FfiConverterInt64.write(value.startedAtMs, into: &buf) + FfiConverterInt64.write(value.lastEventAtMs, into: &buf) + FfiConverterUInt64.write(value.nextSequence, into: &buf) + FfiConverterUInt32.write(value.totalActiveSeconds, into: &buf) + FfiConverterOptionTypeReadingPosition.write(value.lastPosition, into: &buf) + FfiConverterTypeReadingSessionStatus.write(value.status, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingSessionV2_lift(_ buf: RustBuffer) throws -> ReadingSessionV2 { + return try FfiConverterTypeReadingSessionV2.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingSessionV2_lower(_ value: ReadingSessionV2) -> RustBuffer { + return FfiConverterTypeReadingSessionV2.lower(value) +} + + public struct SearchResult: Equatable, Hashable { public var blockId: String public var lineNumber: UInt32? + public var pageNumber: UInt32? + public var chapterId: String? public var snippet: String public var matchStart: UInt64 public var matchEnd: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(blockId: String, lineNumber: UInt32?, snippet: String, matchStart: UInt64, matchEnd: UInt64) { + public init(blockId: String, lineNumber: UInt32?, pageNumber: UInt32?, chapterId: String?, snippet: String, matchStart: UInt64, matchEnd: UInt64) { self.blockId = blockId self.lineNumber = lineNumber + self.pageNumber = pageNumber + self.chapterId = chapterId self.snippet = snippet self.matchStart = matchStart self.matchEnd = matchEnd @@ -793,6 +1243,8 @@ public struct FfiConverterTypeSearchResult: FfiConverterRustBuffer { try SearchResult( blockId: FfiConverterString.read(from: &buf), lineNumber: FfiConverterOptionUInt32.read(from: &buf), + pageNumber: FfiConverterOptionUInt32.read(from: &buf), + chapterId: FfiConverterOptionString.read(from: &buf), snippet: FfiConverterString.read(from: &buf), matchStart: FfiConverterUInt64.read(from: &buf), matchEnd: FfiConverterUInt64.read(from: &buf) @@ -802,6 +1254,8 @@ public struct FfiConverterTypeSearchResult: FfiConverterRustBuffer { public static func write(_ value: SearchResult, into buf: inout [UInt8]) { FfiConverterString.write(value.blockId, into: &buf) FfiConverterOptionUInt32.write(value.lineNumber, into: &buf) + FfiConverterOptionUInt32.write(value.pageNumber, into: &buf) + FfiConverterOptionString.write(value.chapterId, into: &buf) FfiConverterString.write(value.snippet, into: &buf) FfiConverterUInt64.write(value.matchStart, into: &buf) FfiConverterUInt64.write(value.matchEnd, into: &buf) @@ -1249,20 +1703,22 @@ public func FfiConverterTypeMaterialType_lower(_ value: MaterialType) -> RustBuf public enum NoteAnchor: Equatable, Hashable { - case material(materialId: String + case material(materialId: String, positionSnapshot: ReadingPosition? ) - case markdownBlock(materialId: String, blockId: String + case markdownBlock(materialId: String, blockId: String, positionSnapshot: ReadingPosition? ) - case textLine(materialId: String, lineNumber: UInt32 + case textLine(materialId: String, lineNumber: UInt32, positionSnapshot: ReadingPosition? ) - case pdfPage(materialId: String, pageNumber: UInt32 + case pdfPage(materialId: String, pageNumber: UInt32, positionSnapshot: ReadingPosition? ) - case image(materialId: String + case image(materialId: String, positionSnapshot: ReadingPosition? ) - case epubChapter(materialId: String, chapterId: String + case epubChapter(materialId: String, chapterId: String, positionSnapshot: ReadingPosition? ) case knowledgeItem(knowledgeItemId: String ) + case searchResultAnchor(materialId: String, blockId: String?, lineNumber: UInt32?, pageNumber: UInt32?, chapterId: String?, snippet: String + ) @@ -1284,27 +1740,30 @@ public struct FfiConverterTypeNoteAnchor: FfiConverterRustBuffer { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .material(materialId: try FfiConverterString.read(from: &buf) + case 1: return .material(materialId: try FfiConverterString.read(from: &buf), positionSnapshot: try FfiConverterOptionTypeReadingPosition.read(from: &buf) ) - case 2: return .markdownBlock(materialId: try FfiConverterString.read(from: &buf), blockId: try FfiConverterString.read(from: &buf) + case 2: return .markdownBlock(materialId: try FfiConverterString.read(from: &buf), blockId: try FfiConverterString.read(from: &buf), positionSnapshot: try FfiConverterOptionTypeReadingPosition.read(from: &buf) ) - case 3: return .textLine(materialId: try FfiConverterString.read(from: &buf), lineNumber: try FfiConverterUInt32.read(from: &buf) + case 3: return .textLine(materialId: try FfiConverterString.read(from: &buf), lineNumber: try FfiConverterUInt32.read(from: &buf), positionSnapshot: try FfiConverterOptionTypeReadingPosition.read(from: &buf) ) - case 4: return .pdfPage(materialId: try FfiConverterString.read(from: &buf), pageNumber: try FfiConverterUInt32.read(from: &buf) + case 4: return .pdfPage(materialId: try FfiConverterString.read(from: &buf), pageNumber: try FfiConverterUInt32.read(from: &buf), positionSnapshot: try FfiConverterOptionTypeReadingPosition.read(from: &buf) ) - case 5: return .image(materialId: try FfiConverterString.read(from: &buf) + case 5: return .image(materialId: try FfiConverterString.read(from: &buf), positionSnapshot: try FfiConverterOptionTypeReadingPosition.read(from: &buf) ) - case 6: return .epubChapter(materialId: try FfiConverterString.read(from: &buf), chapterId: try FfiConverterString.read(from: &buf) + case 6: return .epubChapter(materialId: try FfiConverterString.read(from: &buf), chapterId: try FfiConverterString.read(from: &buf), positionSnapshot: try FfiConverterOptionTypeReadingPosition.read(from: &buf) ) case 7: return .knowledgeItem(knowledgeItemId: try FfiConverterString.read(from: &buf) ) + case 8: return .searchResultAnchor(materialId: try FfiConverterString.read(from: &buf), blockId: try FfiConverterOptionString.read(from: &buf), lineNumber: try FfiConverterOptionUInt32.read(from: &buf), pageNumber: try FfiConverterOptionUInt32.read(from: &buf), chapterId: try FfiConverterOptionString.read(from: &buf), snippet: try FfiConverterString.read(from: &buf) + ) + default: throw UniffiInternalError.unexpectedEnumCase } } @@ -1313,44 +1772,60 @@ public struct FfiConverterTypeNoteAnchor: FfiConverterRustBuffer { switch value { - case let .material(materialId): + case let .material(materialId,positionSnapshot): writeInt(&buf, Int32(1)) FfiConverterString.write(materialId, into: &buf) + FfiConverterOptionTypeReadingPosition.write(positionSnapshot, into: &buf) - case let .markdownBlock(materialId,blockId): + case let .markdownBlock(materialId,blockId,positionSnapshot): writeInt(&buf, Int32(2)) FfiConverterString.write(materialId, into: &buf) FfiConverterString.write(blockId, into: &buf) + FfiConverterOptionTypeReadingPosition.write(positionSnapshot, into: &buf) - case let .textLine(materialId,lineNumber): + case let .textLine(materialId,lineNumber,positionSnapshot): writeInt(&buf, Int32(3)) FfiConverterString.write(materialId, into: &buf) FfiConverterUInt32.write(lineNumber, into: &buf) + FfiConverterOptionTypeReadingPosition.write(positionSnapshot, into: &buf) - case let .pdfPage(materialId,pageNumber): + case let .pdfPage(materialId,pageNumber,positionSnapshot): writeInt(&buf, Int32(4)) FfiConverterString.write(materialId, into: &buf) FfiConverterUInt32.write(pageNumber, into: &buf) + FfiConverterOptionTypeReadingPosition.write(positionSnapshot, into: &buf) - case let .image(materialId): + case let .image(materialId,positionSnapshot): writeInt(&buf, Int32(5)) FfiConverterString.write(materialId, into: &buf) + FfiConverterOptionTypeReadingPosition.write(positionSnapshot, into: &buf) - case let .epubChapter(materialId,chapterId): + case let .epubChapter(materialId,chapterId,positionSnapshot): writeInt(&buf, Int32(6)) FfiConverterString.write(materialId, into: &buf) FfiConverterString.write(chapterId, into: &buf) + FfiConverterOptionTypeReadingPosition.write(positionSnapshot, into: &buf) case let .knowledgeItem(knowledgeItemId): writeInt(&buf, Int32(7)) FfiConverterString.write(knowledgeItemId, into: &buf) + + case let .searchResultAnchor(materialId,blockId,lineNumber,pageNumber,chapterId,snippet): + writeInt(&buf, Int32(8)) + FfiConverterString.write(materialId, into: &buf) + FfiConverterOptionString.write(blockId, into: &buf) + FfiConverterOptionUInt32.write(lineNumber, into: &buf) + FfiConverterOptionUInt32.write(pageNumber, into: &buf) + FfiConverterOptionString.write(chapterId, into: &buf) + FfiConverterString.write(snippet, into: &buf) + } } } @@ -1371,6 +1846,87 @@ public func FfiConverterTypeNoteAnchor_lower(_ value: NoteAnchor) -> RustBuffer } +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum OfficePreviewStrategy: Equatable, Hashable { + + case platformPreview + case externalOpen + case serverConvertedPdf + case unsupported + + + + + +} + +#if compiler(>=6) +extension OfficePreviewStrategy: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeOfficePreviewStrategy: FfiConverterRustBuffer { + typealias SwiftType = OfficePreviewStrategy + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OfficePreviewStrategy { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .platformPreview + + case 2: return .externalOpen + + case 3: return .serverConvertedPdf + + case 4: return .unsupported + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: OfficePreviewStrategy, into buf: inout [UInt8]) { + switch value { + + + case .platformPreview: + writeInt(&buf, Int32(1)) + + + case .externalOpen: + writeInt(&buf, Int32(2)) + + + case .serverConvertedPdf: + writeInt(&buf, Int32(3)) + + + case .unsupported: + writeInt(&buf, Int32(4)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeOfficePreviewStrategy_lift(_ buf: RustBuffer) throws -> OfficePreviewStrategy { + return try FfiConverterTypeOfficePreviewStrategy.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeOfficePreviewStrategy_lower(_ value: OfficePreviewStrategy) -> RustBuffer { + return FfiConverterTypeOfficePreviewStrategy.lower(value) +} + + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. @@ -1564,6 +2120,94 @@ public func FfiConverterTypeReadingEvent_lower(_ value: ReadingEvent) -> RustBuf } +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum ReadingEventTypeV2: Equatable, Hashable { + + case materialOpened + case materialClosed + case positionChanged + case heartbeat + case markedAsRead + + + + + +} + +#if compiler(>=6) +extension ReadingEventTypeV2: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeReadingEventTypeV2: FfiConverterRustBuffer { + typealias SwiftType = ReadingEventTypeV2 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReadingEventTypeV2 { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .materialOpened + + case 2: return .materialClosed + + case 3: return .positionChanged + + case 4: return .heartbeat + + case 5: return .markedAsRead + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ReadingEventTypeV2, into buf: inout [UInt8]) { + switch value { + + + case .materialOpened: + writeInt(&buf, Int32(1)) + + + case .materialClosed: + writeInt(&buf, Int32(2)) + + + case .positionChanged: + writeInt(&buf, Int32(3)) + + + case .heartbeat: + writeInt(&buf, Int32(4)) + + + case .markedAsRead: + writeInt(&buf, Int32(5)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingEventTypeV2_lift(_ buf: RustBuffer) throws -> ReadingEventTypeV2 { + return try FfiConverterTypeReadingEventTypeV2.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingEventTypeV2_lower(_ value: ReadingEventTypeV2) -> RustBuffer { + return FfiConverterTypeReadingEventTypeV2.lower(value) +} + + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. @@ -1682,6 +2326,80 @@ public func FfiConverterTypeReadingPosition_lower(_ value: ReadingPosition) -> R } +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum ReadingSessionStatus: Equatable, Hashable { + + case active + case paused + case closed + + + + + +} + +#if compiler(>=6) +extension ReadingSessionStatus: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeReadingSessionStatus: FfiConverterRustBuffer { + typealias SwiftType = ReadingSessionStatus + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReadingSessionStatus { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .active + + case 2: return .paused + + case 3: return .closed + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ReadingSessionStatus, into buf: inout [UInt8]) { + switch value { + + + case .active: + writeInt(&buf, Int32(1)) + + + case .paused: + writeInt(&buf, Int32(2)) + + + case .closed: + writeInt(&buf, Int32(3)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingSessionStatus_lift(_ buf: RustBuffer) throws -> ReadingSessionStatus { + return try FfiConverterTypeReadingSessionStatus.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadingSessionStatus_lower(_ value: ReadingSessionStatus) -> RustBuffer { + return FfiConverterTypeReadingSessionStatus.lower(value) +} + + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -1754,6 +2472,31 @@ fileprivate struct FfiConverterOptionTypeReadingPosition: FfiConverterRustBuffer } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceUInt32: FfiConverterRustBuffer { + typealias SwiftType = [UInt32] + + public static func write(_ value: [UInt32], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterUInt32.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [UInt32] { + let len: Int32 = try readInt(&buf) + var seq = [UInt32]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterUInt32.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -1779,6 +2522,81 @@ fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer { } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeEpubChapter: FfiConverterRustBuffer { + typealias SwiftType = [EpubChapter] + + public static func write(_ value: [EpubChapter], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeEpubChapter.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [EpubChapter] { + let len: Int32 = try readInt(&buf) + var seq = [EpubChapter]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeEpubChapter.read(from: &buf)) + } + return seq + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypePdfPageText: FfiConverterRustBuffer { + typealias SwiftType = [PdfPageText] + + public static func write(_ value: [PdfPageText], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypePdfPageText.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [PdfPageText] { + let len: Int32 = try readInt(&buf) + var seq = [PdfPageText]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypePdfPageText.read(from: &buf)) + } + return seq + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeReadingEventV2: FfiConverterRustBuffer { + typealias SwiftType = [ReadingEventV2] + + public static func write(_ value: [ReadingEventV2], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeReadingEventV2.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [ReadingEventV2] { + let len: Int32 = try readInt(&buf) + var seq = [ReadingEventV2]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeReadingEventV2.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -1878,120 +2696,276 @@ fileprivate struct FfiConverterSequenceSequenceString: FfiConverterRustBuffer { return seq } } +public func ackEventsV2(eventIds: [String]) -> UInt32 { + return try! FfiConverterUInt32.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_ack_events_v2( + FfiConverterSequenceString.lower(eventIds),$0 + ) +}) +} +public func cleanupStaleSessionsFfi(nowMs: Int64, maxAgeMs: Int64) -> UInt32 { + return try! FfiConverterUInt32.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_cleanup_stale_sessions_ffi( + FfiConverterInt64.lower(nowMs), + FfiConverterInt64.lower(maxAgeMs),$0 + ) +}) +} public func clearExportedEvents(count: UInt32) {try! rustCall() { uniffi_zx_document_ffi_fn_func_clear_exported_events( FfiConverterUInt32.lower(count),$0 ) } } +public func closeReadingSessionV2(sessionId: String)throws {try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_close_reading_session_v2( + FfiConverterString.lower(sessionId),$0 + ) +} +} public func createNoteAnchor(materialId: String, position: ReadingPosition?) -> NoteAnchor { - let rawBytes = Array(materialId.utf8) - return try! rawBytes.withUnsafeBufferPointer { ptr -> NoteAnchor in - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - var posHasValue: Int8 = 0; var posCap: UInt64 = 0; var posLen: UInt64 = 0 - var posDataPtr = UnsafeRawPointer(bitPattern: 0)! - if let pos = position { - posHasValue = 1 - let posBuf = FfiConverterTypeReadingPosition_lower(pos) - posCap = posBuf.capacity; posLen = posBuf.len; posDataPtr = UnsafeRawPointer(posBuf.data!) - } - ffi_zx_document_ffi_create_note_anchor_separate( - Int32(ptr.count), UnsafeRawPointer(ptr.baseAddress!), - posCap, posLen, posDataPtr, posHasValue, - &outCapacity, &outLen, &outData, &errorCode) - if errorCode != 0 { return NoteAnchor.material(materialId: "") } - return try! FfiConverterTypeNoteAnchor_lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) - } + return try! FfiConverterTypeNoteAnchor_lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_create_note_anchor( + FfiConverterString.lower(materialId), + FfiConverterOptionTypeReadingPosition.lower(position),$0 + ) +}) +} +public func createNoteAnchorFromSearch(materialId: String, result: SearchResult) -> NoteAnchor { + return try! FfiConverterTypeNoteAnchor_lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_create_note_anchor_from_search( + FfiConverterString.lower(materialId), + FfiConverterTypeSearchResult_lower(result),$0 + ) +}) } public func detectMaterialType(filePath: String)throws -> MaterialType { - let rawBytes = Array(filePath.utf8) - return try rawBytes.withUnsafeBufferPointer { ptr -> MaterialType in - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - ffi_zx_document_ffi_detect_material_type_separate(Int32(ptr.count), UnsafeRawPointer(ptr.baseAddress!), &outCapacity, &outLen, &outData, &errorCode) - if errorCode != 0 { throw DocumentError.ParseError(message: "detect failed") } - return try FfiConverterTypeMaterialType.lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) - } + return try FfiConverterTypeMaterialType_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_detect_material_type( + FfiConverterString.lower(filePath),$0 + ) +}) } public func exportPendingEvents() -> [ReadingEvent] { - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - ffi_zx_document_ffi_export_pending_events_separate(&outCapacity, &outLen, &outData, &errorCode) - return try! FfiConverterSequenceTypeReadingEvent.lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) + return try! FfiConverterSequenceTypeReadingEvent.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_export_pending_events($0 + ) +}) +} +public func exportPendingEventsV2(limit: UInt32, timestampMs: Int64) -> [ReadingEventV2] { + return try! FfiConverterSequenceTypeReadingEventV2.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_export_pending_events_v2( + FfiConverterUInt32.lower(limit), + FfiConverterInt64.lower(timestampMs),$0 + ) +}) +} +public func extractPdfTextFfi(filePath: String)throws -> [PdfPageText] { + return try FfiConverterSequenceTypePdfPageText.lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_extract_pdf_text_ffi( + FfiConverterString.lower(filePath),$0 + ) +}) +} +public func getOfficePreviewConfigFfi(materialType: MaterialType, fileSize: UInt64)throws -> OfficePreviewConfig { + return try FfiConverterTypeOfficePreviewConfig_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_get_office_preview_config_ffi( + FfiConverterTypeMaterialType_lower(materialType), + FfiConverterUInt64.lower(fileSize),$0 + ) +}) +} +public func isOfficeTypeFfi(materialType: MaterialType) -> Bool { + return try! FfiConverterBool.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_is_office_type_ffi( + FfiConverterTypeMaterialType_lower(materialType),$0 + ) +}) +} +public func markEventsFailedV2(eventIds: [String]) -> UInt32 { + return try! FfiConverterUInt32.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_mark_events_failed_v2( + FfiConverterSequenceString.lower(eventIds),$0 + ) +}) } public func parseMarkdown(content: String)throws -> [DocumentBlock] { - let rawBytes = Array(content.utf8) - return try rawBytes.withUnsafeBufferPointer { ptr -> [DocumentBlock] in - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - ffi_zx_document_ffi_parse_markdown_separate(Int32(ptr.count), UnsafeRawPointer(ptr.baseAddress!), &outCapacity, &outLen, &outData, &errorCode) - if errorCode != 0 { throw DocumentError.ParseError(message: "parse failed") } - return try FfiConverterSequenceTypeDocumentBlock.lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) - } + return try FfiConverterSequenceTypeDocumentBlock.lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_parse_markdown( + FfiConverterString.lower(content),$0 + ) +}) } public func parseText(content: String)throws -> [DocumentBlock] { - let rawBytes = Array(content.utf8) - return try rawBytes.withUnsafeBufferPointer { ptr -> [DocumentBlock] in - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - ffi_zx_document_ffi_parse_text_separate(Int32(ptr.count), UnsafeRawPointer(ptr.baseAddress!), &outCapacity, &outLen, &outData, &errorCode) - if errorCode != 0 { throw DocumentError.ParseError(message: "parse failed") } - return try FfiConverterSequenceTypeDocumentBlock.lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) - } + return try FfiConverterSequenceTypeDocumentBlock.lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_parse_text( + FfiConverterString.lower(content),$0 + ) +}) } -public func pushReadingEvent(event: ReadingEvent) { - let eventBuf = FfiConverterTypeReadingEvent_lower(event) - ffi_zx_document_ffi_push_reading_event_separate(eventBuf.capacity, eventBuf.len, UnsafeRawPointer(eventBuf.data!)) +public func pauseReadingSessionV2(sessionId: String)throws {try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_pause_reading_session_v2( + FfiConverterString.lower(sessionId),$0 + ) +} +} +public func pushHeartbeatV2(sessionId: String, materialId: String, activeSecondsDelta: UInt32, position: ReadingPosition?, timestampMs: Int64)throws -> ReadingEventV2 { + return try FfiConverterTypeReadingEventV2_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_push_heartbeat_v2( + FfiConverterString.lower(sessionId), + FfiConverterString.lower(materialId), + FfiConverterUInt32.lower(activeSecondsDelta), + FfiConverterOptionTypeReadingPosition.lower(position), + FfiConverterInt64.lower(timestampMs),$0 + ) +}) +} +public func pushMarkedAsReadV2(sessionId: String, materialId: String, timestampMs: Int64)throws -> ReadingEventV2 { + return try FfiConverterTypeReadingEventV2_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_push_marked_as_read_v2( + FfiConverterString.lower(sessionId), + FfiConverterString.lower(materialId), + FfiConverterInt64.lower(timestampMs),$0 + ) +}) +} +public func pushMaterialClosedV2(sessionId: String, materialId: String, activeSecondsDelta: UInt32, timestampMs: Int64)throws -> ReadingEventV2 { + return try FfiConverterTypeReadingEventV2_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_push_material_closed_v2( + FfiConverterString.lower(sessionId), + FfiConverterString.lower(materialId), + FfiConverterUInt32.lower(activeSecondsDelta), + FfiConverterInt64.lower(timestampMs),$0 + ) +}) +} +public func pushMaterialOpenedV2(sessionId: String, materialId: String, timestampMs: Int64)throws -> ReadingEventV2 { + return try FfiConverterTypeReadingEventV2_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_push_material_opened_v2( + FfiConverterString.lower(sessionId), + FfiConverterString.lower(materialId), + FfiConverterInt64.lower(timestampMs),$0 + ) +}) +} +public func pushPositionChangedV2(sessionId: String, materialId: String, position: ReadingPosition, timestampMs: Int64)throws -> ReadingEventV2 { + return try FfiConverterTypeReadingEventV2_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_push_position_changed_v2( + FfiConverterString.lower(sessionId), + FfiConverterString.lower(materialId), + FfiConverterTypeReadingPosition_lower(position), + FfiConverterInt64.lower(timestampMs),$0 + ) +}) +} +public func pushReadingEvent(event: ReadingEvent) {try! rustCall() { + uniffi_zx_document_ffi_fn_func_push_reading_event( + FfiConverterTypeReadingEvent_lower(event),$0 + ) +} +} +public func readEpubChaptersFfi(filePath: String)throws -> [EpubChapter] { + return try FfiConverterSequenceTypeEpubChapter.lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_read_epub_chapters_ffi( + FfiConverterString.lower(filePath),$0 + ) +}) +} +public func readEpubMetadataFfi(filePath: String)throws -> EpubMetadata { + return try FfiConverterTypeEpubMetadata_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_read_epub_metadata_ffi( + FfiConverterString.lower(filePath),$0 + ) +}) } public func readImageMeta(filePath: String)throws -> ImageMeta { - let rawBytes = Array(filePath.utf8) - return try rawBytes.withUnsafeBufferPointer { ptr -> ImageMeta in - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - ffi_zx_document_ffi_read_image_meta_separate(Int32(ptr.count), UnsafeRawPointer(ptr.baseAddress!), &outCapacity, &outLen, &outData, &errorCode) - if errorCode != 0 { throw DocumentError.ParseError(message: "read failed") } - return try FfiConverterTypeImageMeta.lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) - } + return try FfiConverterTypeImageMeta_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_read_image_meta( + FfiConverterString.lower(filePath),$0 + ) +}) +} +public func readPdfMetadataFfi(filePath: String)throws -> PdfMetadata { + return try FfiConverterTypePdfMetadata_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_read_pdf_metadata_ffi( + FfiConverterString.lower(filePath),$0 + ) +}) } public func readTextStats(filePath: String)throws -> TextStats { - let rawBytes = Array(filePath.utf8) - return try rawBytes.withUnsafeBufferPointer { ptr -> TextStats in - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - ffi_zx_document_ffi_read_text_stats_separate(Int32(ptr.count), UnsafeRawPointer(ptr.baseAddress!), &outCapacity, &outLen, &outData, &errorCode) - if errorCode != 0 { throw DocumentError.ParseError(message: "read failed") } - return try FfiConverterTypeTextStats.lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) - } + return try FfiConverterTypeTextStats_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_read_text_stats( + FfiConverterString.lower(filePath),$0 + ) +}) +} +public func reloadStaleEventsV2() -> UInt32 { + return try! FfiConverterUInt32.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_reload_stale_events_v2($0 + ) +}) +} +public func restorePositionFromAnchor(anchor: NoteAnchor) -> ReadingPosition? { + return try! FfiConverterOptionTypeReadingPosition.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_restore_position_from_anchor( + FfiConverterTypeNoteAnchor_lower(anchor),$0 + ) +}) +} +public func resumeReadingSessionV2(sessionId: String)throws {try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_resume_reading_session_v2( + FfiConverterString.lower(sessionId),$0 + ) +} +} +public func searchEpubChaptersFfi(chapterIds: [String], chapterTexts: [String], query: String) -> [SearchResult] { + return try! FfiConverterSequenceTypeSearchResult.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_search_epub_chapters_ffi( + FfiConverterSequenceString.lower(chapterIds), + FfiConverterSequenceString.lower(chapterTexts), + FfiConverterString.lower(query),$0 + ) +}) } public func searchMarkdownBlocks(blocks: [DocumentBlock], query: String) -> [SearchResult] { - let queryBytes = Array(query.utf8) - let blocksBuf = FfiConverterSequenceTypeDocumentBlock.lower(blocks) - return queryBytes.withUnsafeBufferPointer { qPtr -> [SearchResult] in - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - ffi_zx_document_ffi_search_markdown_blocks_separate( - blocksBuf.capacity, blocksBuf.len, UnsafeRawPointer(blocksBuf.data!), - Int32(qPtr.count), UnsafeRawPointer(qPtr.baseAddress!), - &outCapacity, &outLen, &outData, &errorCode) - return try! FfiConverterSequenceTypeSearchResult.lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) - } + return try! FfiConverterSequenceTypeSearchResult.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_search_markdown_blocks( + FfiConverterSequenceTypeDocumentBlock.lower(blocks), + FfiConverterString.lower(query),$0 + ) +}) +} +public func searchPdfPages(pageNumbers: [UInt32], pageTexts: [String], query: String) -> [SearchResult] { + return try! FfiConverterSequenceTypeSearchResult.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_search_pdf_pages( + FfiConverterSequenceUInt32.lower(pageNumbers), + FfiConverterSequenceString.lower(pageTexts), + FfiConverterString.lower(query),$0 + ) +}) } public func searchTextContent(content: String, query: String) -> [SearchResult] { - let contentBytes = Array(content.utf8) - let queryBytes = Array(query.utf8) - return contentBytes.withUnsafeBufferPointer { cPt -> [SearchResult] in - return queryBytes.withUnsafeBufferPointer { qPt -> [SearchResult] in - var outCapacity: UInt64 = 0; var outLen: UInt64 = 0; var outData: UnsafeMutableRawPointer? = nil; var errorCode: Int8 = 0 - ffi_zx_document_ffi_search_text_content_separate( - Int32(cPt.count), UnsafeRawPointer(cPt.baseAddress!), - Int32(qPt.count), UnsafeRawPointer(qPt.baseAddress!), - &outCapacity, &outLen, &outData, &errorCode) - return try! FfiConverterSequenceTypeSearchResult.lift(RustBuffer(capacity: outCapacity, len: outLen, data: outData)) - } - } + return try! FfiConverterSequenceTypeSearchResult.lift(try! rustCall() { + uniffi_zx_document_ffi_fn_func_search_text_content( + FfiConverterString.lower(content), + FfiConverterString.lower(query),$0 + ) +}) +} +public func startReadingSessionV2(material: ReadingMaterialRef, timestampMs: Int64)throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_start_reading_session_v2( + FfiConverterTypeReadingMaterialRef_lower(material), + FfiConverterInt64.lower(timestampMs),$0 + ) +}) +} +public func updateReadingPosition(materialId: String, position: ReadingPosition) {try! rustCall() { + uniffi_zx_document_ffi_fn_func_update_reading_position( + FfiConverterString.lower(materialId), + FfiConverterTypeReadingPosition_lower(position),$0 + ) } -public func updateReadingPosition(materialId: String, position: ReadingPosition) { - let rawBytes = Array(materialId.utf8) - rawBytes.withUnsafeBufferPointer { ptr in - let posBuf = FfiConverterTypeReadingPosition_lower(position) - var errorCode: Int8 = 0 - ffi_zx_document_ffi_update_reading_position_separate( - Int32(ptr.count), UnsafeRawPointer(ptr.baseAddress!), - posBuf.capacity, posBuf.len, UnsafeRawPointer(posBuf.data!), &errorCode) - } } // swiftlint:enable all \ No newline at end of file diff --git a/AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.h b/AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.h index a321e4c..04d1257 100644 --- a/AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.h +++ b/AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.h @@ -242,17 +242,37 @@ typedef struct UniffiForeignFutureResultVoid { typedef void (*UniffiForeignFutureCompleteVoid)(uint64_t, UniffiForeignFutureResultVoid ); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_ACK_EVENTS_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_ACK_EVENTS_V2 +uint32_t uniffi_zx_document_ffi_fn_func_ack_events_v2(RustBuffer event_ids, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEANUP_STALE_SESSIONS_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEANUP_STALE_SESSIONS_FFI +uint32_t uniffi_zx_document_ffi_fn_func_cleanup_stale_sessions_ffi(int64_t now_ms, int64_t max_age_ms, RustCallStatus *_Nonnull out_status +); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS void uniffi_zx_document_ffi_fn_func_clear_exported_events(uint32_t count, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLOSE_READING_SESSION_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLOSE_READING_SESSION_V2 +void uniffi_zx_document_ffi_fn_func_close_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH +RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor_from_search(RustBuffer material_id, RustBuffer result, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE RustBuffer uniffi_zx_document_ffi_fn_func_detect_material_type(RustBuffer file_path, RustCallStatus *_Nonnull out_status @@ -262,6 +282,31 @@ RustBuffer uniffi_zx_document_ffi_fn_func_detect_material_type(RustBuffer file_p #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events(RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS_V2 +RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events_v2(uint32_t limit, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXTRACT_PDF_TEXT_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXTRACT_PDF_TEXT_FFI +RustBuffer uniffi_zx_document_ffi_fn_func_extract_pdf_text_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI +RustBuffer uniffi_zx_document_ffi_fn_func_get_office_preview_config_ffi(RustBuffer material_type, uint64_t file_size, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_IS_OFFICE_TYPE_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_IS_OFFICE_TYPE_FFI +int8_t uniffi_zx_document_ffi_fn_func_is_office_type_ffi(RustBuffer material_type, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_MARK_EVENTS_FAILED_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_MARK_EVENTS_FAILED_V2 +uint32_t uniffi_zx_document_ffi_fn_func_mark_events_failed_v2(RustBuffer event_ids, RustCallStatus *_Nonnull out_status ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN @@ -274,31 +319,107 @@ RustBuffer uniffi_zx_document_ffi_fn_func_parse_markdown(RustBuffer content, Rus RustBuffer uniffi_zx_document_ffi_fn_func_parse_text(RustBuffer content, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PAUSE_READING_SESSION_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PAUSE_READING_SESSION_V2 +void uniffi_zx_document_ffi_fn_func_pause_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_HEARTBEAT_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_HEARTBEAT_V2 +RustBuffer uniffi_zx_document_ffi_fn_func_push_heartbeat_v2(RustBuffer session_id, RustBuffer material_id, uint32_t active_seconds_delta, RustBuffer position, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MARKED_AS_READ_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MARKED_AS_READ_V2 +RustBuffer uniffi_zx_document_ffi_fn_func_push_marked_as_read_v2(RustBuffer session_id, RustBuffer material_id, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_CLOSED_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_CLOSED_V2 +RustBuffer uniffi_zx_document_ffi_fn_func_push_material_closed_v2(RustBuffer session_id, RustBuffer material_id, uint32_t active_seconds_delta, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_OPENED_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_MATERIAL_OPENED_V2 +RustBuffer uniffi_zx_document_ffi_fn_func_push_material_opened_v2(RustBuffer session_id, RustBuffer material_id, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_POSITION_CHANGED_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_POSITION_CHANGED_V2 +RustBuffer uniffi_zx_document_ffi_fn_func_push_position_changed_v2(RustBuffer session_id, RustBuffer material_id, RustBuffer position, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT void uniffi_zx_document_ffi_fn_func_push_reading_event(RustBuffer event, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_CHAPTERS_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_CHAPTERS_FFI +RustBuffer uniffi_zx_document_ffi_fn_func_read_epub_chapters_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_METADATA_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_EPUB_METADATA_FFI +RustBuffer uniffi_zx_document_ffi_fn_func_read_epub_metadata_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META RustBuffer uniffi_zx_document_ffi_fn_func_read_image_meta(RustBuffer file_path, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_PDF_METADATA_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_PDF_METADATA_FFI +RustBuffer uniffi_zx_document_ffi_fn_func_read_pdf_metadata_ffi(RustBuffer file_path, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS RustBuffer uniffi_zx_document_ffi_fn_func_read_text_stats(RustBuffer file_path, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RELOAD_STALE_EVENTS_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RELOAD_STALE_EVENTS_V2 +uint32_t uniffi_zx_document_ffi_fn_func_reload_stale_events_v2(RustCallStatus *_Nonnull out_status + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESTORE_POSITION_FROM_ANCHOR +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESTORE_POSITION_FROM_ANCHOR +RustBuffer uniffi_zx_document_ffi_fn_func_restore_position_from_anchor(RustBuffer anchor, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESUME_READING_SESSION_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_RESUME_READING_SESSION_V2 +void uniffi_zx_document_ffi_fn_func_resume_reading_session_v2(RustBuffer session_id, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_EPUB_CHAPTERS_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_EPUB_CHAPTERS_FFI +RustBuffer uniffi_zx_document_ffi_fn_func_search_epub_chapters_ffi(RustBuffer chapter_ids, RustBuffer chapter_texts, RustBuffer query, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS RustBuffer uniffi_zx_document_ffi_fn_func_search_markdown_blocks(RustBuffer blocks, RustBuffer query, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_PDF_PAGES +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_PDF_PAGES +RustBuffer uniffi_zx_document_ffi_fn_func_search_pdf_pages(RustBuffer page_numbers, RustBuffer page_texts, RustBuffer query, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT RustBuffer uniffi_zx_document_ffi_fn_func_search_text_content(RustBuffer content, RustBuffer query, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_START_READING_SESSION_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_START_READING_SESSION_V2 +RustBuffer uniffi_zx_document_ffi_fn_func_start_reading_session_v2(RustBuffer material, int64_t timestamp_ms, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION void uniffi_zx_document_ffi_fn_func_update_reading_position(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status @@ -562,18 +683,42 @@ void ffi_zx_document_ffi_rust_future_free_void(uint64_t handle #ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID #define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID void ffi_zx_document_ffi_rust_future_complete_void(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_ACK_EVENTS_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_ACK_EVENTS_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_ack_events_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEANUP_STALE_SESSIONS_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEANUP_STALE_SESSIONS_FFI +uint16_t uniffi_zx_document_ffi_checksum_func_cleanup_stale_sessions_ffi(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS uint16_t uniffi_zx_document_ffi_checksum_func_clear_exported_events(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLOSE_READING_SESSION_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLOSE_READING_SESSION_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_close_reading_session_v2(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR_FROM_SEARCH +uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor_from_search(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE @@ -586,6 +731,36 @@ uint16_t uniffi_zx_document_ffi_checksum_func_detect_material_type(void #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXTRACT_PDF_TEXT_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXTRACT_PDF_TEXT_FFI +uint16_t uniffi_zx_document_ffi_checksum_func_extract_pdf_text_ffi(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_GET_OFFICE_PREVIEW_CONFIG_FFI +uint16_t uniffi_zx_document_ffi_checksum_func_get_office_preview_config_ffi(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_IS_OFFICE_TYPE_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_IS_OFFICE_TYPE_FFI +uint16_t uniffi_zx_document_ffi_checksum_func_is_office_type_ffi(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_MARK_EVENTS_FAILED_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_MARK_EVENTS_FAILED_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_mark_events_failed_v2(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN @@ -598,36 +773,126 @@ uint16_t uniffi_zx_document_ffi_checksum_func_parse_markdown(void #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT uint16_t uniffi_zx_document_ffi_checksum_func_parse_text(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PAUSE_READING_SESSION_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PAUSE_READING_SESSION_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_pause_reading_session_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_HEARTBEAT_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_HEARTBEAT_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_push_heartbeat_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MARKED_AS_READ_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MARKED_AS_READ_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_push_marked_as_read_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_CLOSED_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_CLOSED_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_push_material_closed_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_OPENED_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_MATERIAL_OPENED_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_push_material_opened_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_POSITION_CHANGED_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_POSITION_CHANGED_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_push_position_changed_v2(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT uint16_t uniffi_zx_document_ffi_checksum_func_push_reading_event(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_CHAPTERS_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_CHAPTERS_FFI +uint16_t uniffi_zx_document_ffi_checksum_func_read_epub_chapters_ffi(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_METADATA_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_EPUB_METADATA_FFI +uint16_t uniffi_zx_document_ffi_checksum_func_read_epub_metadata_ffi(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META uint16_t uniffi_zx_document_ffi_checksum_func_read_image_meta(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_PDF_METADATA_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_PDF_METADATA_FFI +uint16_t uniffi_zx_document_ffi_checksum_func_read_pdf_metadata_ffi(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS uint16_t uniffi_zx_document_ffi_checksum_func_read_text_stats(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RELOAD_STALE_EVENTS_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RELOAD_STALE_EVENTS_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_reload_stale_events_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESTORE_POSITION_FROM_ANCHOR +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESTORE_POSITION_FROM_ANCHOR +uint16_t uniffi_zx_document_ffi_checksum_func_restore_position_from_anchor(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESUME_READING_SESSION_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_RESUME_READING_SESSION_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_resume_reading_session_v2(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_EPUB_CHAPTERS_FFI +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_EPUB_CHAPTERS_FFI +uint16_t uniffi_zx_document_ffi_checksum_func_search_epub_chapters_ffi(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS uint16_t uniffi_zx_document_ffi_checksum_func_search_markdown_blocks(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_PDF_PAGES +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_PDF_PAGES +uint16_t uniffi_zx_document_ffi_checksum_func_search_pdf_pages(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT #define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT uint16_t uniffi_zx_document_ffi_checksum_func_search_text_content(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_START_READING_SESSION_V2 +#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_START_READING_SESSION_V2 +uint16_t uniffi_zx_document_ffi_checksum_func_start_reading_session_v2(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION diff --git a/AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.modulemap b/AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.modulemap deleted file mode 100644 index f7cb771..0000000 --- a/AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.modulemap +++ /dev/null @@ -1,7 +0,0 @@ -module zx_documentFFI { - header "zx_documentFFI.h" - export * - use "Darwin" - use "_Builtin_stdbool" - use "_Builtin_stdint" -} \ No newline at end of file diff --git a/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/Headers/module.modulemap b/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/Headers/module.modulemap new file mode 100644 index 0000000..9a0bd4e --- /dev/null +++ b/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/Headers/module.modulemap @@ -0,0 +1 @@ +module zx_documentFFI { header "zx_documentFFI.h"; export * } diff --git a/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/Headers/zx_documentFFI.h b/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/Headers/zx_documentFFI.h new file mode 100644 index 0000000..04d1257 --- /dev/null +++ b/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/Headers/zx_documentFFI.h @@ -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 +#include +#include + +// 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 + diff --git a/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/libzx_document_ffi.a b/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/libzx_document_ffi.a index b822232..832324a 100644 Binary files a/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/libzx_document_ffi.a and b/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator/libzx_document_ffi.a differ diff --git a/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64/libzx_document_ffi.a b/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64/libzx_document_ffi.a index 812fb39..05df97d 100644 Binary files a/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64/libzx_document_ffi.a and b/AIStudyApp/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64/libzx_document_ffi.a differ diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialReaderView.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialReaderView.swift index c32af26..8dfe539 100644 --- a/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialReaderView.swift +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialReaderView.swift @@ -105,7 +105,8 @@ struct MaterialReaderView: View { private let title: String private let knowledgeBaseId: String? - // Event collector — records reading events during the session + // V2 session manager (primary) + V1 collector (fallback) + private let sessionManager = ReadingRuntimeSessionManager.shared private let collector = ReadingEventCollector.shared private let positionStore = ReadingPositionStore.shared @@ -181,16 +182,11 @@ struct MaterialReaderView: View { ) } .onAppear { - // FIXME: collector calls Rust FFI with struct-passing (broken on ARM64 iOS) - // collector.open(materialId: vm.materialId) + openReadingSession() hasRestoredPosition = false } .onDisappear { - // FIXME: collector calls Rust FFI with struct-passing (broken on ARM64 iOS) - // if let lastPos = collector.lastPosition { - // positionStore.save(materialId: vm.materialId, position: lastPos) - // } - // _ = collector.close(materialId: vm.materialId) + closeReadingSession() } .onChange(of: vm.loadingState) { _, newState in if newState == .loaded, !hasRestoredPosition { @@ -229,7 +225,7 @@ struct MaterialReaderView: View { } ForEach(Array(vm.blocks.enumerated()), id: \.offset) { _, block in DocumentBlockView(block: block) - .id(blockId(from: block)) + .id(ReadingPositionAdapter.blockId(from: block)) } } .padding(.horizontal, 20).padding(.top, 8).padding(.bottom, 100) @@ -259,9 +255,48 @@ struct MaterialReaderView: View { } } + // MARK: - Reading Session Lifecycle + + private func openReadingSession() { + let context = ReadingMaterialContext( + readingTargetType: knowledgeBaseId != nil ? .knowledgeSource : .temporaryFile, + materialId: vm.materialId, + knowledgeBaseId: knowledgeBaseId, + title: title.isEmpty ? nil : title + ) + + // V2 primary + if let _ = try? sessionManager.openMaterial(context) { + print("[READER] V2 session opened — materialId=\(vm.materialId)") + return + } + // V1 fallback + print("[READER] V2 unavailable, falling back to V1") + collector.open(materialId: vm.materialId) + } + + private func closeReadingSession() { + // V2 primary + if sessionManager.state == .active || sessionManager.state == .paused { + if let lastPos = sessionManager.lastPosition { + positionStore.save(materialId: vm.materialId, position: lastPos) + } + sessionManager.closeMaterial() + print("[READER] V2 session closed") + return + } + // V1 fallback + if let lastPos = collector.lastPosition { + positionStore.save(materialId: vm.materialId, position: lastPos) + } + _ = collector.close(materialId: vm.materialId) + print("[READER] V1 session closed") + } + /// Build a NoteAnchor from the current scroll position (for quick note). private func buildAnchor() -> NoteAnchor? { - guard let pos = collector.lastPosition else { return nil } + let pos = sessionManager.lastPosition ?? collector.lastPosition + guard let pos else { return nil } return createNoteAnchor(materialId: vm.materialId, position: pos) } @@ -274,45 +309,34 @@ struct MaterialReaderView: View { case .text(let lineNumber, _): let idx = Int(lineNumber) - 1 if idx >= 0, idx < vm.blocks.count { - restoreBlockId = blockId(from: vm.blocks[idx]) + restoreBlockId = ReadingPositionAdapter.blockId(from: vm.blocks[idx]) } case .pdf, .image, .epub, .unknown: break // PDF/image restoration needs platform-specific handling } } - /// Report current scroll position to the event collector. + /// Report current scroll position to the active session manager. private func reportScrollPosition() { guard !vm.blocks.isEmpty else { return } let idx = max(0, min(vm.blocks.count - 1, Int(scrollProgress * CGFloat(vm.blocks.count)))) - let block = vm.blocks[idx] - let bId = blockId(from: block) let sp = Float(scrollProgress) - let pos: ReadingPosition - switch vm.materialType { - case .markdown: - pos = .markdown(blockId: bId, scrollProgress: sp) - case .text: - pos = .text(lineNumber: UInt32(idx + 1), scrollProgress: sp) - default: - pos = .markdown(blockId: bId, scrollProgress: sp) - } - collector.updatePosition(materialId: vm.materialId, position: pos) - } + guard let pos = ReadingPositionAdapter.fromBlockScroll( + materialType: vm.materialType, + blocks: vm.blocks, + scrollProgress: sp, + blockIndex: idx + ) else { return } - private func blockId(from block: DocumentBlock) -> String { - switch block { - case .heading(let id, _, _): return id - case .paragraph(let id, _): return id - case .list(let id, _, _): return id - case .codeBlock(let id, _, _): return id - case .quote(let id, _): return id - case .table(let id, _, _): return id - case .imageBlock(let id, _, _): return id - case .horizontalRule(let id): return id + // V2 primary + if sessionManager.state == .active { + sessionManager.updatePosition(pos) + } else { + // V1 fallback + collector.updatePosition(materialId: vm.materialId, position: pos) } } diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/QuickNoteSheet.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/QuickNoteSheet.swift index 3b636ed..488b7b6 100644 --- a/AIStudyApp/AIStudyApp/Features/MaterialReader/QuickNoteSheet.swift +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/QuickNoteSheet.swift @@ -123,11 +123,11 @@ struct QuickNoteSheet: View { private var anchorDescription: String? { guard let a = anchor else { return nil } switch a { - case .markdownBlock(_, let blockId): + case .markdownBlock(_, let blockId, _): return "block \(String(blockId.prefix(8)))" - case .textLine(_, let lineNumber): + case .textLine(_, let lineNumber, _): return "第 \(lineNumber) 行" - case .pdfPage(_, let pageNumber): + case .pdfPage(_, let pageNumber, _): return "第 \(pageNumber) 页" default: return nil @@ -137,13 +137,14 @@ struct QuickNoteSheet: View { private func anchorFields(from anchor: NoteAnchor?) -> (type: String?, blockId: String?, line: UInt32?, page: UInt32?) { guard let a = anchor else { return (nil, nil, nil, nil) } switch a { - case .material: return ("material", nil, nil, nil) - case .markdownBlock(_, let bid): return ("markdown", bid, nil, nil) - case .textLine(_, let ln): return ("text", nil, ln, nil) - case .pdfPage(_, let pn): return ("pdf", nil, nil, pn) - case .image: return ("image", nil, nil, nil) - case .epubChapter(_, let cid): return ("epub", cid, nil, nil) - case .knowledgeItem: return ("knowledge_item", nil, nil, nil) + case .material: return ("material", nil, nil, nil) + case .markdownBlock(_, let bid, _): return ("markdown", bid, nil, nil) + case .textLine(_, let ln, _): return ("text", nil, ln, nil) + case .pdfPage(_, let pn, _): return ("pdf", nil, nil, pn) + case .image: return ("image", nil, nil, nil) + case .epubChapter(_, let cid, _): return ("epub", cid, nil, nil) + case .knowledgeItem: return ("knowledge_item", nil, nil, nil) + case .searchResultAnchor: return ("search_result", nil, nil, nil) } } diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventCollector.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventCollector.swift index 010926c..bb0e8ee 100644 --- a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventCollector.swift +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventCollector.swift @@ -1,16 +1,17 @@ import SwiftUI -// MARK: - Reading Event Collector +// MARK: - Reading Event Collector (V1 — deprecated) -/// Collects reading events (MaterialOpened, MaterialClosed, PositionChanged, -/// Heartbeat, MarkedAsRead) into the Rust event buffer for cross-platform export. +/// V1 event collector using deprecated `push_reading_event` / `export_pending_events`. +/// ⚠️ Migrate to V2 `ReadingRuntimeSessionManager` which uses session-aware +/// `push_material_opened_v2` / `export_pending_events_v2` / `ack_events_v2`. +/// +/// V1 is kept as a fallback for environments where V2 FFI is not available. @MainActor final class ReadingEventCollector { static let shared = ReadingEventCollector() - /// The last known reading position (for save-on-exit). private(set) var lastPosition: ReadingPosition? - private var activeMaterialId: String? private var activeSeconds: UInt32 = 0 private var heartbeatTimer: Timer? @@ -18,10 +19,12 @@ final class ReadingEventCollector { private init() {} - // MARK: - Public API - /// Call when a material is opened. + @available(*, deprecated, message: "Use ReadingRuntimeSessionManager.openMaterial() with V2 FFI") func open(materialId: String) { + if #available(iOS 16, *) { + print("[ReadingEvent] ⚠️ V1 collector active — consider upgrading to V2 ReadingRuntimeSessionManager") + } flush() // commit any prior session first activeMaterialId = materialId activeSeconds = 0 diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventMapper.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventMapper.swift new file mode 100644 index 0000000..9e1d224 --- /dev/null +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventMapper.swift @@ -0,0 +1,58 @@ +import Foundation + +// MARK: - ReadingEventMapper + +/// Maps Rust `ReadingEventV2` to API `ReadingEventUploadItem`, +/// supplementing iOS-specific fields (readingTargetType, platform, appVersion, timezone). +enum ReadingEventMapper { + + /// Rust ReadingEventTypeV2 → API snake_case string. + static func apiEventType(_ eventType: ReadingEventTypeV2) -> String { + switch eventType { + case .materialOpened: return "material_opened" + case .materialClosed: return "material_closed" + case .positionChanged: return "position_changed" + case .heartbeat: return "heartbeat" + case .markedAsRead: return "marked_as_read" + } + } + + /// Batch map: Rust events → API upload items. Skips events without context. + static func map( + rustEvents: [ReadingEventV2], + contexts: [String: ReadingMaterialContext], + appVersion: String = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0" + ) -> [ReadingEventUploadItem] { + return rustEvents.compactMap { event in + let ctx = contexts[event.materialId] + return mapOne(event: event, context: ctx, appVersion: appVersion) + } + } + + /// Map a single event. Returns nil if no context found (event will be dropped). + static func mapOne( + event: ReadingEventV2, + context: ReadingMaterialContext?, + appVersion: String = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0" + ) -> ReadingEventUploadItem? { + guard let ctx = context else { + print("[Mapper] ⚠️ No context for materialId=\(event.materialId), skipping event \(event.eventId)") + return nil + } + + return ReadingEventUploadItem( + eventId: event.eventId, + clientSessionId: event.clientSessionId, + materialId: event.materialId, + eventType: apiEventType(event.eventType), + position: event.position, + activeSecondsDelta: Int(event.activeSecondsDelta), + clientTimestampMs: event.timestampMs, + sequence: event.sequence, + readingTargetType: ctx.readingTargetType.rawValue, + platform: "ios", + appVersion: appVersion, + clientTimezoneOffsetMinutes: TimeZone.current.secondsFromGMT() / 60 + ) + } +} diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventUploadQueue.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventUploadQueue.swift new file mode 100644 index 0000000..8a84dd5 --- /dev/null +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventUploadQueue.swift @@ -0,0 +1,245 @@ +import Foundation + +// MARK: - Queue Item + +struct ReadingEventQueueItem: Codable, Equatable, Identifiable { + let id: String // UUID + let eventId: String // Rust ReadingEventV2.eventId + let payload: ReadingEventUploadItem + var status: QueueItemStatus + var retryCount: Int + var lastErrorCode: String? + var lastTriedAt: Date? + let createdAt: Date + var updatedAt: Date + + enum QueueItemStatus: String, Codable { + case pending + case uploading + case failed // retryable + case failedPermanent // will not retry + } +} + +// MARK: - Upload Queue + +/// Local queue for reading event upload items. Persisted to JSON file. +/// Coordinates with Rust buffer: export → enqueue → API upload → ack/markFailed. +@MainActor +final class ReadingEventUploadQueue { + static let shared = ReadingEventUploadQueue() + + private var items: [ReadingEventQueueItem] = [] + private let maxRetryCount = 3 + private let storageURL: URL + + var pendingCount: Int { items.filter { $0.status == .pending }.count } + var failedCount: Int { items.filter { $0.status == .failed }.count } + + private init() { + let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + storageURL = docs.appendingPathComponent("reading_event_queue.json") + loadFromDisk() + } + + // MARK: - Public API + + /// Enqueue a batch of upload items. Deduplicates by eventId. + func enqueue(_ uploadItems: [ReadingEventUploadItem]) { + let existingIds = Set(items.map(\.eventId)) + let now = Date() + let newItems = uploadItems + .filter { !existingIds.contains($0.eventId) } + .map { item in + ReadingEventQueueItem( + id: UUID().uuidString, + eventId: item.eventId, + payload: item, + status: .pending, + retryCount: 0, + lastErrorCode: nil, + lastTriedAt: nil, + createdAt: now, + updatedAt: now + ) + } + items.append(contentsOf: newItems) + saveToDisk() + } + + /// Fetch a batch of pending items (max `limit`). + func fetchPendingBatch(limit: Int = 100) -> [ReadingEventQueueItem] { + return Array(items.filter { $0.status == .pending }.prefix(limit)) + } + + /// Mark items as uploaded (will be removed from queue). + func markUploaded(ids: [String]) { + items.removeAll { ids.contains($0.id) } + saveToDisk() + } + + /// Mark items for retry (increment retry count, set failed status). + func markRetry(ids: [String], errorCode: String? = nil) { + let now = Date() + for i in items.indices { + guard ids.contains(items[i].id) else { continue } + items[i].retryCount += 1 + items[i].lastErrorCode = errorCode + items[i].lastTriedAt = now + items[i].updatedAt = now + items[i].status = items[i].retryCount >= maxRetryCount ? .failedPermanent : .failed + } + saveToDisk() + } + + /// Mark items as permanently failed (will not retry). + func markPermanentFailed(ids: [String], errorCode: String? = nil) { + let now = Date() + for i in items.indices { + guard ids.contains(items[i].id) else { continue } + items[i].status = .failedPermanent + items[i].lastErrorCode = errorCode + items[i].lastTriedAt = now + items[i].updatedAt = now + } + saveToDisk() + } + + /// Retry all failed items (move back to pending). + func retryFailed() { + let now = Date() + for i in items.indices { + guard items[i].status == .failed else { continue } + items[i].status = .pending + items[i].updatedAt = now + } + saveToDisk() + } + + /// Remove all permanently failed items. + func clearPermanentFailed() { + items.removeAll { $0.status == .failedPermanent } + saveToDisk() + } + + /// Remove all items. + func clearAll() { + items.removeAll() + saveToDisk() + } + + // MARK: - Persistence + + private func saveToDisk() { + do { + let data = try JSONEncoder().encode(items) + try data.write(to: storageURL, options: .atomic) + } catch { + print("[UploadQueue] Failed to save: \(error)") + } + } + + private func loadFromDisk() { + guard FileManager.default.fileExists(atPath: storageURL.path) else { + items = [] + return + } + do { + let data = try Data(contentsOf: storageURL) + items = try JSONDecoder().decode([ReadingEventQueueItem].self, from: data) + // Recover stuck uploading items (crash recovery) + let now = Date() + for i in items.indices where items[i].status == .uploading { + items[i].status = .pending + items[i].updatedAt = now + } + print("[UploadQueue] Loaded \(items.count) items from disk") + } catch { + print("[UploadQueue] Failed to load: \(error)") + items = [] + } + } +} + +// MARK: - Upload Pipeline + +/// Orchestrates: Rust export → mapper → enqueue → API upload → ack/markFailed. +@MainActor +final class ReadingEventUploadPipeline { + static let shared = ReadingEventUploadPipeline() + + private let queue = ReadingEventUploadQueue.shared + private let adapter: ReadingRuntimeAdapter = RustReadingRuntimeAdapter() + private let readingAPI = ReadingAPIService.shared + + private init() {} + + /// Full pipeline: export from Rust → enqueue. + func exportAndEnqueue(contexts: [String: ReadingMaterialContext]) { + let rustEvents = adapter.exportEvents(limit: 100, timestampMs: nowMs()) + guard !rustEvents.isEmpty else { return } + + let uploadItems = ReadingEventMapper.map(rustEvents: rustEvents, contexts: contexts) + guard !uploadItems.isEmpty else { return } + + queue.enqueue(uploadItems) + print("[UploadPipeline] Exported \(rustEvents.count) events, enqueued \(uploadItems.count)") + } + + /// Flush pending items to API. + func flush() async { + let batch = queue.fetchPendingBatch(limit: 100) + guard !batch.isEmpty else { return } + + queue.markUploading(ids: batch.map(\.id)) + + do { + let response = try await readingAPI.uploadReadingEvents(batch.map(\.payload)) + + // Ack successful events in Rust + let ackedIds = batch.map(\.eventId) + _ = adapter.ackEvents(ackedIds) + + queue.markUploaded(ids: batch.map(\.id)) + print("[UploadPipeline] Flushed \(response.processed) events") + } catch let error as APIError { + let errorCode = error.errorCode ?? "NETWORK_ERROR" + queue.markRetry(ids: batch.map(\.id), errorCode: errorCode) + _ = adapter.markFailed(batch.map(\.eventId)) + print("[UploadPipeline] Flush failed: \(errorCode)") + } catch { + queue.markRetry(ids: batch.map(\.id), errorCode: "UNKNOWN") + _ = adapter.markFailed(batch.map(\.eventId)) + } + } + + /// Reload on app launch: reload stale Rust events, enqueue, retry failed. + func reloadOnLaunch(contexts: [String: ReadingMaterialContext]) { + _ = adapter.reloadStaleEvents() + _ = adapter.cleanupStaleSessions(nowMs: nowMs(), maxAgeMs: 30 * 60 * 1000) + + exportAndEnqueue(contexts: contexts) + queue.retryFailed() + } + + // MARK: - Helpers + + private func nowMs() -> Int64 { + Int64(Date().timeIntervalSince1970 * 1000) + } +} + +// MARK: - Queue Helpers + +extension ReadingEventUploadQueue { + func markUploading(ids: [String]) { + let now = Date() + for i in items.indices { + guard ids.contains(items[i].id) else { continue } + items[i].status = .uploading + items[i].lastTriedAt = now + items[i].updatedAt = now + } + saveToDisk() + } +} diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingPositionAdapter.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingPositionAdapter.swift new file mode 100644 index 0000000..299ed58 --- /dev/null +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingPositionAdapter.swift @@ -0,0 +1,97 @@ +import Foundation + +// MARK: - Reading Position Adapter + +/// Generates the correct `ReadingPosition` variant for each material type. +/// Handles progress clamping (0~1) and camelCase field mapping. +struct ReadingPositionAdapter { + + /// Build a ReadingPosition from the current viewport state. + static func buildPosition( + materialType: MaterialType, + blockId: String? = nil, + scrollProgress: Float? = nil, + lineNumber: UInt32? = nil, + pageNumber: UInt32? = nil, + pageProgress: Float? = nil, + overallProgress: Float? = nil, + chapterId: String? = nil, + chapterProgress: Float? = nil, + zoomScale: Float? = nil, + offsetX: Float? = nil, + offsetY: Float? = nil + ) -> ReadingPosition? { + switch materialType { + case .markdown: + guard let bid = blockId else { return nil } + return .markdown(blockId: bid, scrollProgress: clamp(scrollProgress ?? 0)) + + case .text: + return .text(lineNumber: lineNumber ?? 1, scrollProgress: clamp(scrollProgress ?? 0)) + + case .pdf: + return .pdf( + pageNumber: pageNumber ?? 1, + pageProgress: clamp(pageProgress ?? 0), + overallProgress: clamp(overallProgress ?? 0) + ) + + case .image: + return .image( + zoomScale: zoomScale ?? 1.0, + offsetX: offsetX ?? 0, + offsetY: offsetY ?? 0 + ) + + case .epub: + guard let cid = chapterId else { return nil } + return .epub( + chapterId: cid, + chapterProgress: clamp(chapterProgress ?? 0), + overallProgress: clamp(overallProgress ?? 0) + ) + + case .word, .excel, .powerPoint, .unknown: + return .unknown + } + } + + /// Build a Markdown position from block index + scroll progress. + static func fromBlockScroll( + materialType: MaterialType, + blocks: [DocumentBlock], + scrollProgress: Float, + blockIndex: Int + ) -> ReadingPosition? { + guard !blocks.isEmpty, blockIndex >= 0, blockIndex < blocks.count else { return nil } + let bId = blockId(from: blocks[blockIndex]) + + return buildPosition( + materialType: materialType, + blockId: bId, + scrollProgress: clamp(scrollProgress), + lineNumber: UInt32(blockIndex + 1) + ) + } + + // MARK: - Helpers + + private static func clamp(_ value: Float) -> Float { + if value.isNaN || value < 0 { return 0 } + if value > 1 { return 1 } + return value + } + + static func blockId(from block: DocumentBlock) -> String { + switch block { + case .heading(let id, _, _): return id + case .paragraph(let id, _): return id + case .list(let id, _, _): return id + case .codeBlock(let id, _, _): return id + case .quote(let id, _): return id + case .table(let id, _, _): return id + case .imageBlock(let id, _, _): return id + case .horizontalRule(let id): return id + } + } +} diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingRuntimeSessionManager.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingRuntimeSessionManager.swift new file mode 100644 index 0000000..b9bd4db --- /dev/null +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingRuntimeSessionManager.swift @@ -0,0 +1,175 @@ +import Foundation + +// MARK: - Session State + +enum ReadingSessionState { + case idle + case active + case paused + case closed +} + +// MARK: - ReadingRuntimeSessionManager + +/// V2 session-aware manager using the ReadingRuntimeAdapter protocol. +/// Replaces the deprecated V1 ReadingEventCollector. +@MainActor +final class ReadingRuntimeSessionManager { + static let shared = ReadingRuntimeSessionManager() + + // MARK: - Public State + + private(set) var state: ReadingSessionState = .idle + private(set) var activeSessionId: String? + private(set) var activeContext: ReadingMaterialContext? + private(set) var lastPosition: ReadingPosition? + + // MARK: - Dependencies + + var adapter: ReadingRuntimeAdapter = RustReadingRuntimeAdapter() + + // MARK: - Private + + private var heartbeatTimer: Timer? + private var positionDebounceTask: Task? + private var lastHeartbeatAtMs: Int64 = 0 + + private init() {} + + // MARK: - Session Lifecycle + + /// Start a reading session for a material. + /// - Throws if a session is already active (must close first). + func openMaterial(_ context: ReadingMaterialContext) throws { + guard state == .idle || state == .closed else { + throw SessionError.alreadyActive + } + + let material = ReadingMaterialRef(materialId: context.materialId) + let now = nowMs() + + let sessionId = try adapter.startSession(material: material, timestampMs: now) + activeSessionId = sessionId + activeContext = context + state = .active + lastPosition = nil + lastHeartbeatAtMs = now + + // Push MaterialOpened + _ = try adapter.pushOpened(sessionId: sessionId, materialId: context.materialId, timestampMs: now) + + startHeartbeat() + } + + /// Close the current session. Can be called from any non-idle state. + func closeMaterial() { + guard state != .idle, let sessionId = activeSessionId, let ctx = activeContext else { return } + + stopHeartbeat() + flushPosition() + + let now = nowMs() + let sinceLastTick = lastHeartbeatAtMs > 0 ? UInt32((now - lastHeartbeatAtMs) / 1000) : 0 + _ = try? adapter.pushClosed(sessionId: sessionId, materialId: ctx.materialId, delta: sinceLastTick, timestampMs: now) + _ = try? adapter.closeSession(sessionId) + + activeSessionId = nil + activeContext = nil + lastPosition = nil + state = .closed + } + + func pause() { + guard state == .active, let sessionId = activeSessionId else { return } + stopHeartbeat() + flushPosition() + _ = try? adapter.pauseSession(sessionId) + state = .paused + } + + func resume() { + guard state == .paused, let sessionId = activeSessionId else { return } + _ = try? adapter.resumeSession(sessionId) + state = .active + startHeartbeat() + } + + // MARK: - Position + + func updatePosition(_ position: ReadingPosition) { + guard state == .active, let sessionId = activeSessionId, let ctx = activeContext else { return } + lastPosition = position + + positionDebounceTask?.cancel() + positionDebounceTask = Task { [weak self] in + try? await Task.sleep(nanoseconds: 2_000_000_000) + guard let self, !Task.isCancelled, let pos = self.lastPosition, + let sid = self.activeSessionId, let c = self.activeContext else { return } + _ = try? self.adapter.pushPositionChanged( + sessionId: sid, materialId: c.materialId, + position: pos, timestampMs: self.nowMs() + ) + } + } + + func markAsRead() { + guard state == .active, let sessionId = activeSessionId, let ctx = activeContext else { return } + _ = try? adapter.pushMarkedAsRead(sessionId: sessionId, materialId: ctx.materialId, timestampMs: nowMs()) + } + + // MARK: - Heartbeat + + private func startHeartbeat(interval: TimeInterval = 15) { + stopHeartbeat() + heartbeatTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in + Task { @MainActor [weak self] in + guard let self, + self.state == .active, + let sid = self.activeSessionId, + let ctx = self.activeContext else { return } + let now = self.nowMs() + _ = try? self.adapter.pushHeartbeat( + sessionId: sid, materialId: ctx.materialId, + delta: UInt32(interval), position: self.lastPosition, + timestampMs: now + ) + self.lastHeartbeatAtMs = now + } + } + } + + private func stopHeartbeat() { + heartbeatTimer?.invalidate() + heartbeatTimer = nil + } + + private func flushPosition() { + positionDebounceTask?.cancel() + // Force-send last position before clearing + if let pos = lastPosition, let sid = activeSessionId, let ctx = activeContext { + _ = try? adapter.pushPositionChanged( + sessionId: sid, materialId: ctx.materialId, + position: pos, timestampMs: nowMs() + ) + } + positionDebounceTask = nil + } + + // MARK: - Helpers + + private func nowMs() -> Int64 { + Int64(Date().timeIntervalSince1970 * 1000) + } +} + +// MARK: - Errors + +enum SessionError: Error, LocalizedError { + case alreadyActive + + var errorDescription: String? { + switch self { + case .alreadyActive: return "A reading session is already active. Close it first." + } + } +} diff --git a/AIStudyApp/docs/ios-learning-info-design.md b/AIStudyApp/docs/ios-learning-info-design.md new file mode 100644 index 0000000..c7b5692 --- /dev/null +++ b/AIStudyApp/docs/ios-learning-info-design.md @@ -0,0 +1,315 @@ +# iOS 学习信息采集 总设计 + +> IOS-INFO-000 | v1.0 | 2026-06-08 + +## 1. 概述 + +iOS 端学习信息采集系统的目标:从 MaterialReader 生命周期的各个环节采集阅读行为数据,经 Rust document runtime 生成事件,再经本地上传队列可靠投递到 API 服务端。 + +### 职责边界 + +``` +┌─────────────────────────────────────────────────────────┐ +│ iOS App │ +│ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ +│ │ Reader View │──▶│ Rust Runtime │──▶│ UploadQueue │ │ +│ │ (Lifecycle) │ │ (Event V2) │ │ (ack/retry) │ │ +│ └─────────────┘ └──────────────┘ └──────┬──────┘ │ +│ │ │ +│ ┌─────────────┐ ┌──────────────┐ │ │ +│ │ Continue │ │ Progress │ │ │ +│ │ Learning │ │ Restore │ │ │ +│ └─────────────┘ └──────────────┘ │ │ +└───────────────────────────────────────────────┼────────┘ + │ + POST /learning/reading-events/batch + │ + ┌───────▼────────┐ + │ API Server │ + │ (M8) │ + └────────────────┘ +``` + +**Rust 负责:** 事件生成(UUID/sequence/delta)、position normalize、session 管理、buffer 状态机 +**iOS 负责:** 生命周期触发、readingTargetType 补充、上传队列、ack/reload、位置缓存、UI 联动 + +## 2. V1 → V2 迁移 + +### 当前状态(V1) + +```swift +// ReadingEventCollector.swift — V1 API +pushReadingEvent(event: ReadingEvent.materialOpened(...)) +exportPendingEvents() // → [ReadingEvent] +clearExportedEvents(count: n) // 无 ack,无重试 +``` + +### 致命问题(F1-F4 审查发现) + +| ID | 问题 | 影响 | +|----|------|------| +| F1 | `open/close` 被注释 | 事件链路断裂,无数据 | +| F2 | 无上传队列 | 事件丢失无重试 | +| F3 | 继续学习硬编码 | 无法动态更新 | +| F4 | 位置恢复仅本地 | 跨设备无法同步 | + +### 目标状态(V2) + +```swift +// ReadingRuntimeSessionManager.swift — V2 API +let sid = try startReadingSessionV2(material: material, timestampMs: now) +let ev = try pushMaterialOpenedV2(sessionId: sid, materialId: id, timestampMs: now) +let batch = exportPendingEventsV2(limit: 100, timestampMs: now) +ackEventsV2(eventIds: ids) // 成功后确认删除 +markEventsFailedV2(eventIds: ids) // 失败后标记重试 +reloadStaleEventsV2() // App 启动恢复 +``` + +### 迁移对照 + +| V1 API (废弃) | V2 API (使用) | +|--------------|-------------| +| `pushReadingEvent(ReadingEvent.materialOpened)` | `pushMaterialOpenedV2(sessionId, materialId, ts)` | +| `pushReadingEvent(ReadingEvent.heartbeat)` | `pushHeartbeatV2(sessionId, materialId, delta, pos, ts)` | +| `exportPendingEvents()` | `exportPendingEventsV2(limit, ts)` | +| `clearExportedEvents(count)` | `ackEventsV2(eventIds)` | +| — | `markEventsFailedV2(eventIds)` | +| — | `reloadStaleEventsV2()` | +| `activeSeconds` (累计) | `activeSecondsDelta` (增量) | + +## 3. 核心类型 + +### ReadingMaterialContext + +```swift +struct ReadingMaterialContext { + let materialId: String + let readingTargetType: ReadingTargetType // knowledge_source | temporary_file + let title: String? + let knowledgeBaseId: String? + let materialType: MaterialType // Rust MaterialType + let previewMode: PreviewMode // Rust PreviewMode +} + +enum ReadingTargetType: String { + case knowledgeSource = "knowledge_source" + case temporaryFile = "temporary_file" +} +``` + +### ReadingRuntimeAdapter 协议 + +```swift +protocol ReadingRuntimeAdapter { + // Session + func startSession(material: ReadingMaterialRef, timestampMs: Int64) throws -> String + func closeSession(_ sessionId: String) throws + func pauseSession(_ sessionId: String) throws + func resumeSession(_ sessionId: String) throws + + // Events + func pushOpened(sessionId: String, materialId: String, timestampMs: Int64) throws -> ReadingEventV2 + func pushClosed(sessionId: String, materialId: String, delta: UInt32, timestampMs: Int64) throws -> ReadingEventV2 + func pushPositionChanged(sessionId: String, materialId: String, position: ReadingPosition, timestampMs: Int64) throws -> ReadingEventV2 + func pushHeartbeat(sessionId: String, materialId: String, delta: UInt32, position: ReadingPosition?, timestampMs: Int64) throws -> ReadingEventV2 + func pushMarkedAsRead(sessionId: String, materialId: String, timestampMs: Int64) throws -> ReadingEventV2 + + // Buffer + func exportEvents(limit: UInt32, timestampMs: Int64) -> [ReadingEventV2] + func ackEvents(_ eventIds: [String]) -> UInt32 + func markFailed(_ eventIds: [String]) -> UInt32 + func reloadStale() -> UInt32 + func cleanupStaleSessions(nowMs: Int64, maxAgeMs: Int64) -> UInt32 +} +``` + +### ReadingEventUploadItem + +```swift +struct ReadingEventUploadItem: Codable { + // From Rust ReadingEventV2 + let eventId: String + let clientSessionId: String + let materialId: String + let eventType: String + let position: ReadingPosition? + let activeSecondsDelta: Int + let clientTimestampMs: Int64 + let sequence: UInt64 + + // iOS supplements + let readingTargetType: String + let platform: String = "ios" + let appVersion: String + let clientTimezoneOffsetMinutes: Int +} +``` + +## 4. 架构:ReadingRuntimeSessionManager + +```swift +@MainActor +final class ReadingRuntimeSessionManager { + static let shared = ReadingRuntimeSessionManager() + + private var activeSessionId: String? + private var context: ReadingMaterialContext? + private var heartbeatTimer: Timer? + private var lastPosition: ReadingPosition? + + // MARK: - Lifecycle + + func openMaterial(_ ctx: ReadingMaterialContext) + func closeMaterial() + func appDidEnterBackground() + func appWillEnterForeground() + + // MARK: - Position + func updatePosition(_ pos: ReadingPosition) // debounced + func markAsRead() + + // MARK: - Heartbeat + func startHeartbeat(interval: TimeInterval = 15) + func stopHeartbeat() +} +``` + +## 5. 上传队列:ReadingEventUploadQueue + +### 状态机 + +``` + Rust export + │ + ▼ + LocalQueue (Swift array, persisted to disk) + │ + ▼ + POST /learning/reading-events/batch + │ + ┌─────┴─────┐ + ▼ ▼ + 成功(200) 失败/网络错误 + │ │ + ▼ ▼ + ackEvents markFailed + (Rust delete) (Rust retry mark) + │ │ + ▼ ▼ + remove from keep in queue + local queue for next attempt +``` + +### UploadQueue 核心方法 + +```swift +final class ReadingEventUploadQueue { + func enqueue(_ items: [ReadingEventUploadItem]) + func flush() async -> FlushResult // called on timer + app background + func retryFailed() async -> FlushResult // called on network recovery + func reloadOnLaunch() // reloadStaleEventsV2 + load persisted +} + +struct FlushResult { + let uploaded: Int + let failed: Int + let warnings: [String] +} +``` + +## 6. 关键时机 + +| 时机 | 触发方 | 动作 | +|------|--------|------| +| MaterialReader onAppear | SwiftUI | `openMaterial(ctx)` | +| MaterialReader onDisappear | SwiftUI | `closeMaterial()` | +| Scroll/page change | Reader View | `updatePosition(pos)` | +| Tap "Mark as Read" | UI Button | `markAsRead()` | +| App → background | ScenePhase | `flush()` + stop heartbeat | +| App → foreground | ScenePhase | `reloadStale()` + `cleanupStaleSessions()` | +| Network recovery | NWPathMonitor | `retryFailed()` | +| Every 30s (timer) | UploadScheduler | `flush()` | +| On launch | App init | `reloadOnLaunch()` | + +## 7. 继续学习 + +### 当前(F3 硬编码) + +```swift +// 硬编码"推荐继续阅读"文案,无数据源 +``` + +### 目标 + +```swift +struct ContinueLearningService { + func fetch() async -> ContinueLearningItem? { + let resp = try await api.get("/learning/continue") + switch resp.type { + case "knowledge_source", "temporary_file": + return ContinueLearningItem( + materialId: resp.materialId, + title: resp.title ?? "Untitled", + lastPosition: resp.lastPosition, + lastProgress: resp.lastProgress + ) + case "none": + return nil + default: + return nil + } + } +} +``` + +## 8. 阅读位置恢复 + +### 当前(F4 仅本地) + +```swift +// ReadingPositionStore: UserDefaults only +``` + +### 目标 + +```swift +struct ReadingPositionRestoreService { + func restore(for materialId: String, targetType: ReadingTargetType) async -> ReadingPosition? { + // 1. Try API: GET /materials/:id/reading-progress + if let remote = try? await api.getProgress(materialId, targetType), + remote.status != "not_started", + let pos = remote.lastPosition { + return pos + } + // 2. Fallback: local cache (ReadingPositionStore) + return ReadingPositionStore.shared.get(for: materialId) + } +} +``` + +## 9. 分析页对齐 + +| API | iOS 界面 | +|-----|---------| +| GET /learning/summary | AnalysisHomeView 顶部卡片 | +| GET /learning/trend?days=7 | 趋势曲线 | +| GET /activity/heatmap?days=365 | 热力图 | + +## 10. 错误处理 + +| 错误码 | iOS 动作 | +|--------|---------| +| MATERIAL_ACCESS_DENIED | 不重试,从队列移除,toast 提示 | +| TEMPORARY_MATERIAL_EXPIRED | 从队列移除 | +| DUPLICATE_EVENT | 直接 ack 移除 | +| SOURCE_DELETED | toast 提示,从队列移除 | +| INVALID_* | 从队列移除,记录日志 | +| 网络错误 | 保留队列,等网络恢复后 retry | +| 5xx | 指数退避重试(1s/2s/4s/8s max 3 retry) | + +## 11. 离线策略 + +- 事件始终写入 Rust buffer + 本地队列 +- 无网络时 export 继续执行(Rust buffer 正常),upload 跳过 +- 网络恢复时 `reloadStaleEventsV2()` + `flush()` +- 本地队列持久化到文件(JSON),App 重启可恢复