zhixi-document-runtime/docs/uniffi-udl-spec.md
wangdl fcb01f29c3
Some checks failed
CI/CD Pipeline / Lint (push) Has been cancelled
CI/CD Pipeline / Test (push) Has been cancelled
CI/CD Pipeline / Build (push) Has been cancelled
CI/CD Pipeline / Release (macos-15, aarch64-apple-ios, aarch64-apple-ios) (push) Has been cancelled
CI/CD Pipeline / Release (macos-15, aarch64-apple-ios-sim, aarch64-apple-ios-sim) (push) Has been cancelled
CI/CD Pipeline / Package xcframework (push) Has been cancelled
fix: UniFFI version 0.28→0.31 in docs
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 15:06:06 +08:00

6.4 KiB
Raw Permalink Blame History

UniFFI UDL 编写规范 + 代码生成流程

1. 概述

UniFFIUniversal Foreign Function Interface是 Mozilla 开发的跨语言绑定生成工具。本项目使用 UniFFI 0.31 从 Rust 生成 Swift 绑定。

UDLUniFFI Definition Language文件定义跨语言接口。


2. UDL 语法规范

2.1 命名空间

namespace zx_document {
    // 导出函数
};

命名空间名即 Swift 模块名(zx_document)。

2.2 函数声明

// 基础函数
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

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_idmaterialId

EnumRust enum → Swift enum

[Enum]
interface MaterialType {
    Markdown();
    Text();
    Pdf();
    Image();
    Epub();
    Word();
    Excel();
    PowerPoint();
    Unknown();
};

带数据的枚举

[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

[Error]
enum DocumentError {
    "FileNotFound",
    "UnsupportedFormat",
    "ParseError",
    "InvalidEncoding",
    "IoError",
};

对应 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/ 定义类型
    #[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 代码生成

# 从 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 验证

# 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 重新导出

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

dictionary Foo {
    string bar_baz;   // ✅ 对应 Rust foo.bar_baz
    string barBaz;    // ❌ 不匹配
};

4.3 UniFFI 版本兼容

UniFFI 版本 Rust crate CLI
0.31 uniffi = "0.31" uniffi-bindgen 0.31

版本必须一致。检查:

cargo tree -p zx_document_ffi | grep uniffi
uniffi-bindgen --version

4.4 Optional 类型

// UDL: T? = Rust: Option<T>
u32? page_count;            // → Swift: public var pageCount: UInt32?
sequence<string>? items;    // → Swift: public var items: [String]?

5. 生成代码结构

// 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. 相关文档