# 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/)