feat: M1.1 完成 + M1.2 启动 — 全量更新

M1.1 收尾:
- 24项 P0/P1/P2 bug 修复 (Rust 107 tests + Flutter 15 tests)
- Flutter App v0.3: cupertino_icons 修复, 单元测试, 调试面板, APK 52.6MB
- 示例插件完善: manifest.json + 请求/响应示范 + 7个测试
- API 文档重写 (以 routes.rs 为唯一权威)
- MILESTONES.md 更新至 100%

M1.2 启动:
- P0: 插件管理 API 闭环 (handle_manager_message Custom 分支 + broadcast_plugin_states)
- ServiceManager 集成测试 8/8 (tests/m1_2_service_manager.rs)
- M1.2 测试计划 (docs/M1.2_TEST_PLAN.md, 18个E2E场景)
- 动态插件系统: auto_rollback + version_manager GC + 路径穿越防护

总计: Rust 115/115 测试, Flutter 15/15 测试, 零 warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
showen
2026-03-14 18:12:42 +08:00
parent 8ed9cb2d9d
commit d30c111c71
68 changed files with 8115 additions and 1201 deletions

View File

@@ -296,6 +296,15 @@ pub type FfiStr = *const c_char;
///
/// 主程序从插件取回 JSON 或错误信息时使用该结构体。内存由插件分配,
/// 再通过 `PluginVTable::free_string` 释放。
///
/// 该类型故意不携带 allocator 标识,以保持现有 `repr(C)` ABI 稳定;
/// 因此调用方必须通过 API 契约保证“谁分配,谁释放”。
///
/// # Safety
/// - 插件返回给主程序的 `FfiString` 只能由当前插件导出的
/// `PluginVTable::free_string` 释放。
/// - 主程序分配的字符串绝不能交给插件释放,反之亦然。
/// - 跨 allocator 释放会导致未定义行为(可能崩溃或内存损坏)。
#[repr(C)]
pub struct FfiString {
/// 字符串起始指针;为空时表示没有内容。
@@ -308,6 +317,7 @@ impl FfiString {
/// 从 Rust `String` 构造 FFI 字符串。
///
/// 如果字符串中包含内部 `NUL` 字节,会返回空字符串表示失败。
/// 生成的指针必须由当前插件的 `free_string` 回调释放。
pub fn from_string(s: String) -> Self {
match CString::new(s) {
Ok(cstr) => {
@@ -332,7 +342,8 @@ impl FfiString {
/// 复制为 Rust String不释放底层内存
///
/// # Safety
/// ptr 必须指向有效 null-terminated C 字符串
/// `ptr` 必须指向由当前插件 allocator 创建的有效 null-terminated C 字符串
/// 调用此函数不会转移所有权,原始分配方仍负责释放该内存。
pub unsafe fn to_string(&self) -> Option<String> {
if self.ptr.is_null() {
return None;
@@ -376,6 +387,8 @@ impl FfiResult {
/// 主程序提供给插件的消息发送回调。
///
/// 插件通常无需直接调用该类型,而是通过 [`MessageSender`] 使用安全封装。
/// 若插件把发送器保存到后台线程,必须在 [`ShowenPlugin::stop`] 返回前终止这些线程,
/// 之后不得再继续使用该回调或 `MessageSender`。
pub type SendCallback = unsafe extern "C" fn(ctx: *mut c_void, envelope_json: FfiStr);
/// 插件导出给主程序的函数表。
@@ -403,6 +416,9 @@ pub struct PluginVTable {
/// 停止插件。
pub stop: unsafe extern "C" fn(handle: PluginHandle) -> FfiResult,
/// 释放由插件返回的字符串。
///
/// # Safety
/// 只能释放当前插件导出的 `FfiString`。不要将宿主分配的字符串传给这里。
pub free_string: unsafe extern "C" fn(s: FfiString),
/// 销毁插件实例。
pub destroy: unsafe extern "C" fn(handle: PluginHandle),
@@ -729,7 +745,8 @@ pub trait ShowenPlugin: Send {
/// 停止插件并释放运行期资源。
///
/// 该方法通常用于停止后台线程、撤销监听、关闭文件句柄或网络连接。执行完成后,
/// 主程序可能很快销毁插件实例。
/// 主程序可能很快销毁插件实例。所有后台线程必须在该方法返回前退出,并停止使用
/// 之前在 `init` 中收到的 `MessageSender`。
///
/// # Examples
/// ```ignore