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:
parent
9ab9a1a052
commit
c15a3bbb9e
273
docs/uniffi-udl-spec.md
Normal file
273
docs/uniffi-udl-spec.md
Normal file
@ -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>` | `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` |
|
||||||
|
| 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/)
|
||||||
Loading…
x
Reference in New Issue
Block a user