diff --git a/docs/uniffi-udl-spec.md b/docs/uniffi-udl-spec.md new file mode 100644 index 0000000..f2318d9 --- /dev/null +++ b/docs/uniffi-udl-spec.md @@ -0,0 +1,273 @@ +# UniFFI UDL 编写规范 + 代码生成流程 + +## 1. 概述 + +UniFFI(Universal Foreign Function Interface)是 Mozilla 开发的跨语言绑定生成工具。本项目使用 UniFFI 0.28 从 Rust 生成 Swift 绑定。 + +UDL(UniFFI Definition Language)文件定义跨语言接口。 + +--- + +## 2. UDL 语法规范 + +### 2.1 命名空间 + +```webidl +namespace zx_document { + // 导出函数 +}; +``` + +命名空间名即 Swift 模块名(`zx_document`)。 + +### 2.2 函数声明 + +```webidl +// 基础函数 +MaterialType detect_material_type([ByRef] string file_path); + +// 可抛异常 +[Throws=DocumentError] +DocumentInfo build_document_info([ByRef] string file_path, string material_id, string title); + +// 无返回值 +void push_reading_event(ReadingEvent event); +``` + +#### 参数修饰符 + +| 修饰符 | 适用类型 | 说明 | +|--------|----------|------| +| `[ByRef]` | `string` | 传递引用而非所有权转移(性能优化) | +| 无修饰符 | 所有类型 | 所有权转移给被调用方 | + +### 2.3 数据类型 + +#### Dictionary(Record → Swift struct) + +```webidl +dictionary DocumentInfo { + string material_id; + string title; + MaterialType material_type; + u32? page_count; // Optional + string? image_format; // Optional +}; + +// Rust 侧: #[derive(uniffi::Record)] pub struct DocumentInfo { ... } +// Swift 侧: public struct DocumentInfo { public var materialId: String, ... } +``` + +**命名规则**: +- UDL: `snake_case` → Swift: `camelCase` +- 示例:`material_id` → `materialId` + +#### Enum(Rust enum → Swift enum) + +```webidl +[Enum] +interface MaterialType { + Markdown(); + Text(); + Pdf(); + Image(); + Epub(); + Word(); + Excel(); + PowerPoint(); + Unknown(); +}; +``` + +**带数据的枚举**: +```webidl +[Enum] +interface ReadingPosition { + Markdown(string block_id, f32 scroll_progress); + Text(u32 line_number, f32 scroll_progress); + Pdf(u32 page_number, f32 page_progress, f32 overall_progress); + Unknown(); // 无数据变体 +}; +``` + +#### Error(Rust enum → Swift Error) + +```webidl +[Error] +enum DocumentError { + "FileNotFound", + "UnsupportedFormat", + "ParseError", + "InvalidEncoding", + "IoError", +}; +``` + +**对应 Rust**: +```rust +#[derive(Debug, thiserror::Error, uniffi::Error)] +pub enum DocumentError { + #[error("File not found")] FileNotFound, + #[error("Unsupported format")] UnsupportedFormat, + // ... +} +``` + +### 2.4 类型映射表 + +| UDL | Rust | Swift | +|-----|------|-------| +| `string` | `String` | `String` | +| `u32` | `u32` | `UInt32` | +| `u64` | `u64` | `UInt64` | +| `i64` | `i64` | `Int64` | +| `f32` | `f32` | `Float` | +| `boolean` | `bool` | `Bool` | +| `T?` | `Option` | `T?` | +| `sequence` | `Vec` | `[T]` | +| `dictionary` | `#[derive(Record)] struct` | `struct` | +| `[Enum] interface` | `#[derive(Enum)] enum` | `enum` | +| `[Error] enum` | `#[derive(Error)] enum` | `Error` | + +--- + +## 3. 新增类型 Checklist + +### 3.1 Rust 侧 + +1. 在 `zx_document_core/src/` 定义类型 + ```rust + #[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)] + pub struct NewType { ... } + ``` +2. 在 `zx_document_core/src/lib.rs` 添加 `pub mod new_module;` +3. 在 `zx_document_ffi/src/lib.rs` 添加 `pub use zx_document_core::new_module::NewType;` + +### 3.2 UDL 侧 + +1. 在 `zx_document_ffi/src/zx_document.udl` 末尾添加类型定义 +2. 如新增函数,在 `namespace zx_document { }` 内声明 + +### 3.3 代码生成 + +```bash +# 从 workspace root 执行 +cd zhixi-document-runtime + +# 生成 Swift 绑定 +uniffi-bindgen generate \ + crates/zx_document_ffi/src/zx_document.udl \ + --language swift \ + --out-dir ../ios-projects/AIStudyApp/AIStudyApp/Core/ + +# 检查生成结果 +ls ../ios-projects/AIStudyApp/AIStudyApp/Core/zx_document.swift +``` + +### 3.4 验证 + +```bash +# Rust 编译 +cargo build --release -p zx_document_ffi + +# Rust 测试 +cargo test -p zx_document_core + +# iOS 编译(在 ios-projects 目录) +xcodebuild build -project AIStudyApp.xcodeproj -scheme AIStudyApp -destination 'platform=iOS Simulator,name=iPhone 16' +``` + +--- + +## 4. 常见陷阱 + +### 4.1 类型未导出 + +**症状**:`error: cannot find type X in this scope` + +**原因**:Rust 类型未从 `zx_document_ffi` 重新导出 + +```rust +// zx_document_ffi/src/lib.rs — 必须添加 +pub use zx_document_core::new_module::NewType; +``` + +### 4.2 UDL 字段名不一致 + +**症状**:uniffi-bindgen 报 field name mismatch + +**原因**:UDL 字段名必须与 Rust struct 字段名一致(Rust 用 snake_case → UDL 也用 snake_case) + +```webidl +dictionary Foo { + string bar_baz; // ✅ 对应 Rust foo.bar_baz + string barBaz; // ❌ 不匹配 +}; +``` + +### 4.3 UniFFI 版本兼容 + +| UniFFI 版本 | Rust crate | CLI | +|:--:|------|-----| +| 0.28 | `uniffi = "0.28"` | `uniffi-bindgen 0.28` | + +版本必须一致。检查: +```bash +cargo tree -p zx_document_ffi | grep uniffi +uniffi-bindgen --version +``` + +### 4.4 Optional 类型 + +```webidl +// UDL: T? = Rust: Option +u32? page_count; // → Swift: public var pageCount: UInt32? +sequence? items; // → Swift: public var items: [String]? +``` + +--- + +## 5. 生成代码结构 + +```swift +// zx_document.swift (auto-generated by uniffi-bindgen) + +// 1. Type definitions +public struct DocumentInfo { ... } +public enum MaterialType { ... } + +// 2. Protocol + FFI callback +private protocol zx_documentProtocol { ... } + +// 3. Top-level functions +public func detectMaterialType(filePath: String) throws -> MaterialType { ... } +public func buildDocumentInfo(filePath: String, materialId: String, title: String) throws -> DocumentInfo { ... } + +// 4. Internal FFI bridge +private var ffi_zx_document_ffi_*: ...; +``` + +**不要手动修改** `zx_document.swift`。所有修改在 Rust 侧完成后重新生成。 + +--- + +## 6. 当前接口统计 + +| 类别 | 数量 | 示例 | +|------|:--:|------| +| 顶层函数 | 28 | `detect_material_type`, `parse_markdown` | +| Record(struct) | 10 | `DocumentInfo`, `SearchResult` | +| Enum | 7 | `MaterialType`, `ReadingPosition` | +| Error 变体 | 5 | `FileNotFound`, `ParseError` | +| V2 Session 函数 | 6 | `start_reading_session_v2` | +| V2 Event 函数 | 5 | `push_heartbeat_v2` | +| V2 Buffer 函数 | 4 | `export_pending_events_v2` | + +--- + +## 7. 相关文档 + +- [iOS FFI 调用指南](./ios-ffi-integration-guide.md) +- [文件格式支持矩阵](./file-format-support-matrix.md) +- [Rust FFI 绑定更新流程](../ios-projects/docs/rust-ffi-update.md)(ios-projects 仓库) +- [UniFFI 官方文档](https://mozilla.github.io/uniffi-rs/)