docs: add UniFFI UDL spec + codegen guide (DOC-FULL-030)

- UDL syntax: namespace, functions, dictionary, enum, error
- Type mapping table: UDL→Rust→Swift
- New type checklist: Rust→UDL→codegen→verify
- Common pitfalls: type exports, naming, version compatibility
- Current interface stats: 28 functions, 10 records, 7 enums

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-18 14:41:44 +08:00
parent a7b941bd42
commit c1bac717fd

273
docs/uniffi-udl-spec.md Normal file
View File

@ -0,0 +1,273 @@
# UniFFI UDL 编写规范 + 代码生成流程
## 1. 概述
UniFFIUniversal Foreign Function Interface是 Mozilla 开发的跨语言绑定生成工具。本项目使用 UniFFI 0.28 从 Rust 生成 Swift 绑定。
UDLUniFFI 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 数据类型
#### DictionaryRecord → 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`
#### EnumRust 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(); // 无数据变体
};
```
#### ErrorRust 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>` | `T?` |
| `sequence<T>` | `Vec<T>` | `[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<T>
u32? page_count; // → Swift: public var pageCount: UInt32?
sequence<string>? 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` |
| Recordstruct | 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/)