# App ↔ Rust 调用协议 ## 概述 本文档定义宿主 App(iOS/Android)与 Rust Core 之间的全部调用边界。 核心原则: ```text Rust Core 是纯计算层。 不做网络请求、不保存 Token、不访问后端 API。 文件由 App 下载到本地后,把路径交给 Rust。 阅读事件由 Rust 生成,App 负责上传。 ``` --- ## 调用方向 ```text App ──调用──→ Rust Core App ←──返回── Rust Core App ──上传──→ Backend API ``` --- ## Rust 暴露给 App 的函数 所有函数通过 `#[uniffi::export]` proc-macro 标注,经 UDL bindgen 生成 Swift/Kotlin 绑定。 ### 1. 文件类型识别 ```rust fn detect_material_type(file_path: String) -> Result ``` **输出**:MaterialType 枚举值 **用途**:App 据此决定使用哪种 PreviewMode ### 2. 图片 Metadata ```rust fn read_image_meta(file_path: String) -> Result ``` **输出**:width, height, format, file_size ### 3. 文本统计 ```rust fn read_text_stats(file_path: String) -> Result ``` **输出**:line_count, word_count ### 4. 解析 Markdown ```rust fn parse_markdown(content: String) -> Result, DocumentError> ``` **输出**:DocumentBlock 列表(8 种 block 类型) ### 5. 解析纯文本 ```rust fn parse_text(content: String) -> Result, DocumentError> ``` **输出**:段落 block 列表 ### 6. 搜索 Markdown Blocks ```rust fn search_markdown_blocks(blocks: Vec, query: String) -> Vec ``` **输出**:SearchResult 列表(block_id, snippet, match range) ### 7. 搜索纯文本 ```rust fn search_text_content(content: String, query: String) -> Vec ``` **输出**:SearchResult 列表(line_number, snippet, match range) ### 8. 创建笔记锚点 ```rust fn create_note_anchor(material_id: String, position: Option) -> NoteAnchor ``` **输出**:NoteAnchor(从 ReadingPosition 自动映射) ### 9. 推送阅读事件 ```rust fn push_reading_event(event: ReadingEvent) ``` **用途**:将事件推入全局缓冲区 ### 10. 更新阅读位置 ```rust fn update_reading_position(material_id: String, position: ReadingPosition) ``` **用途**:生成 PositionChanged 事件并推入缓冲区 ### 11. 导出待上报事件 ```rust fn export_pending_events() -> Vec ``` **输出**:所有未上报事件(不清空缓冲区) ### 12. 清空已上报事件 ```rust fn clear_exported_events(count: u32) ``` **用途**:确认前 count 条已成功上传,从缓冲区移除 --- ## Rust 不提供的函数(由 App 负责) 以下函数**不存在于 Rust Core**,完全由宿主 App 实现: | App 职责 | 说明 | |---------|------| | `download_file(url)` | COS 下载 → 本地沙盒 | | `upload_reading_events(events)` | POST 到 /reading/events | | `save_note_to_backend(note)` | POST 到 /notes | | `call_ai_chat(prompt)` | 调 AI API | | `share_file(material_id)` | 系统分享面板 | | `delete_material(material_id)` | 调后端删除接口 | | `get_user_token()` | Token 管理 | | `open_system_preview(file_path)` | QuickLook / 系统预览 | --- ## 调用时序 ### 打开并阅读 Markdown ```text 1. App: download_file(cos_url) → /tmp/doc.md 2. App: detect_material_type("/tmp/doc.md") → Markdown 3. App: open_document("/tmp/doc.md", "abc123") → handle 4. App: get_document_info(handle) → DocumentInfo 5. App: get_markdown_blocks(handle) → [Block] 6. App: 渲染 blocks 7. 用户滚动 → App: update_reading_position("abc123", pos) 8. 定时器 → App: export_pending_events() → [events] 9. App: POST /reading/events 10. App: clear_exported_events(n) 11. 用户关闭 → App: export_pending_events() → last events 12. App: POST /reading/events 13. App: clear_exported_events(n) ``` ### 打开 PDF(平台预览) ```text 1. App: download_file(cos_url) → /tmp/doc.pdf 2. App: detect_material_type("/tmp/doc.pdf") → Pdf 3. App: 判断 PreviewMode::PlatformPreview 4. App: open_system_preview("/tmp/doc.pdf") // QuickLook 5. App: 监听页码变化 → 转换为 PdfReadingPosition 6. App: update_reading_position("abc123", pos) 7. 同上导出事件流程 ``` --- ## 错误处理约定 所有函数返回 `Result`: | 错误 | 场景 | App 处理 | |------|------|---------| | FileNotFound | 文件路径不存在 | 提示用户重新下载 | | UnsupportedFormat | 无法识别的格式 | 显示"暂不支持" | | ParseError | 文件内容损坏 | 显示"文件异常" | | InvalidEncoding | 非 UTF-8 | 尝试其他编码或提示 | | IoError | 磁盘错误 | 重试或提示 | --- ## 线程安全 - Rust Core 的函数应在**后台线程**调用 - App 主线程只做 UI 渲染 - `export_pending_events` 和 `clear_exported_events` 内部需要 Mutex 保护