From 138fa61b84a1b4338bc0755caa3c3c820c02a1bb Mon Sep 17 00:00:00 2001 From: Dooling <3426823@qq.com> Date: Wed, 26 Aug 2026 09:27:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20driver-contract=20/=20driver-abi=20?= =?UTF-8?q?=E6=8B=86=E5=88=86=20+=20driver-sdk=20=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E5=A4=96=E5=A3=B3=EF=BC=88V2=20PR-2=EF=BC=8C=C2=A76.1/=C2=A76.?= =?UTF-8?q?2/=C2=A76.8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增两个 crate,类型本体从 driver-sdk 迁出(git mv 保留历史): - driver-contract(§6.1):Rust Driver 语义契约——items/results/ capabilities/Driver trait + 新增调用级错误模型 error.rs (DriverErrorCategory / DriverCallError,§28)。不依赖 driver-abi、 不含 libloading 与 Tokio 调度类型;Raw 类型仍 re-export observation-model,不复制事实来源。 - driver-abi(§6.2):稳定 C ABI v1 类型——FfiStr/FfiSlice/ FfiOwnedBuffer/DriverHandle/DriverApiV1 + tag/envelope。envelope 内嵌 的语义类型引用 driver-contract(方向符合 §36:仅禁止反向)。 - driver-sdk:改为纯 re-export 兼容外壳(§6.8 Transitional), driver_sdk::* 全部既有导入路径保持有效;Manifest v1 作为 legacy 模块暂留(Phase 3 由 driver-package Manifest v2 取代);Driver trait 文档标记 Transitional。 验收(§37.2 前半):四个现有 Driver、driver-loader、device-manager、 poll-engine、control-engine、collector 零改动编译通过(drivers//apps/ 等消费方 git status 为空)。门禁四件套全绿:fmt / check --all-targets --all-features / test --workspace --all-features(841 通过 0 失败)/ clippy -D warnings 0 警告;新 crate cargo doc 零警告;只读构建复验通过。 Refs: docs/FORGLINK_RUNTIME_V2_IMPLEMENTATION_PLAN.md §37.2、§46; docs/architecture/runtime-v2.md 进度表已同步。 --- Cargo.lock | 23 ++++ crates/driver-abi/Cargo.toml | 12 ++ .../src/abi => driver-abi/src}/envelope.rs | 8 +- crates/driver-abi/src/lib.rs | 23 ++++ .../src/abi => driver-abi/src}/tag.rs | 0 .../src/abi.rs => driver-abi/src/v1.rs} | 3 - crates/driver-contract/Cargo.toml | 13 ++ .../src/capabilities.rs | 0 .../src/driver.rs | 0 crates/driver-contract/src/error.rs | 129 ++++++++++++++++++ .../src/items.rs | 0 crates/driver-contract/src/lib.rs | 39 ++++++ .../src/results.rs | 0 crates/driver-sdk/Cargo.toml | 4 +- crates/driver-sdk/src/lib.rs | 117 ++++++++++++---- crates/driver-sdk/src/manifest_v1.rs | 61 +++++++++ docs/architecture/runtime-v2.md | 4 +- 17 files changed, 399 insertions(+), 37 deletions(-) create mode 100644 crates/driver-abi/Cargo.toml rename crates/{driver-sdk/src/abi => driver-abi/src}/envelope.rs (98%) create mode 100644 crates/driver-abi/src/lib.rs rename crates/{driver-sdk/src/abi => driver-abi/src}/tag.rs (100%) rename crates/{driver-sdk/src/abi.rs => driver-abi/src/v1.rs} (99%) create mode 100644 crates/driver-contract/Cargo.toml rename crates/{driver-sdk => driver-contract}/src/capabilities.rs (100%) rename crates/{driver-sdk => driver-contract}/src/driver.rs (100%) create mode 100644 crates/driver-contract/src/error.rs rename crates/{driver-sdk => driver-contract}/src/items.rs (100%) create mode 100644 crates/driver-contract/src/lib.rs rename crates/{driver-sdk => driver-contract}/src/results.rs (100%) create mode 100644 crates/driver-sdk/src/manifest_v1.rs diff --git a/Cargo.lock b/Cargo.lock index a776c3a..eaa1d02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,6 +353,27 @@ dependencies = [ "serde_json", ] +[[package]] +name = "driver-abi" +version = "0.1.0" +dependencies = [ + "driver-contract", + "observation-model", + "serde", + "serde_json", +] + +[[package]] +name = "driver-contract" +version = "0.1.0" +dependencies = [ + "async-trait", + "observation-model", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "driver-ether-ip" version = "0.1.0" @@ -431,6 +452,8 @@ name = "driver-sdk" version = "0.1.0" dependencies = [ "async-trait", + "driver-abi", + "driver-contract", "observation-model", "serde", "serde_json", diff --git a/crates/driver-abi/Cargo.toml b/crates/driver-abi/Cargo.toml new file mode 100644 index 0000000..8e946e6 --- /dev/null +++ b/crates/driver-abi/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "driver-abi" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true + +[dependencies] +observation-model = { path = "../observation-model" } +driver-contract = { path = "../driver-contract" } +serde = { version = "1", features = ["derive"] } +serde_json = "1" diff --git a/crates/driver-sdk/src/abi/envelope.rs b/crates/driver-abi/src/envelope.rs similarity index 98% rename from crates/driver-sdk/src/abi/envelope.rs rename to crates/driver-abi/src/envelope.rs index cc48122..5fc4169 100644 --- a/crates/driver-sdk/src/abi/envelope.rs +++ b/crates/driver-abi/src/envelope.rs @@ -20,12 +20,12 @@ use observation_model::{DriverErrorInfo, RawReadResult}; use serde::{Deserialize, Deserializer, Serialize}; -use crate::items::DriverCommand; -use crate::results::{ +use driver_contract::items::DriverCommand; +use driver_contract::results::{ AddressMetadata, DriverBrowseNode, RawCommandResult, RawEvent, RawHistoryPage, RawWriteResult, SubscriptionRequest, }; -use crate::{HistoryRequest, ProtocolCapabilities}; +use driver_contract::{HistoryRequest, ProtocolCapabilities}; /// 本 SDK 实现的 ABI 版本对应的 Envelope schema 版本(ABI 1.0)。 pub const SCHEMA_VERSION: &str = "1.0"; @@ -268,7 +268,7 @@ mod tests { use observation_model::{RawValue, TimestampNs}; use super::*; - use crate::results::RawEventKind; + use driver_contract::results::RawEventKind; #[test] fn read_envelope_exact_json() { diff --git a/crates/driver-abi/src/lib.rs b/crates/driver-abi/src/lib.rs new file mode 100644 index 0000000..7680406 --- /dev/null +++ b/crates/driver-abi/src/lib.rs @@ -0,0 +1,23 @@ +//! driver-abi:跨动态库边界的稳定 C ABI 类型(Runtime V2 方案 §6.2 Normative)。 +//! +//! 本 crate 是原 `driver-sdk::abi` 的目标归属,专门保存 C ABI 类型: +//! `FfiStr` / `FfiSlice` / `FfiOwnedBuffer`、`DriverHandle`、`DriverApiV1`、 +//! ABI tag / envelope 与 entry symbol 常量。 +//! +//! # 规则(§6.2) +//! +//! - 所有跨 ABI 类型必须 `#[repr(C)]` 或显式 `ptr + len`; +//! - `driver-contract` 不依赖本 crate(§36 依赖方向约束);envelope JSON 中 +//! 内嵌的语义类型(`DriverCommand` 等)引用自 `driver-contract`, +//! 不在此复制第二份定义; +//! - `native-driver-loader` 和 Native Driver 可以依赖本 crate; +//! - ABI v1/v2 在同一 crate 内按模块版本化,但不得把 loader 行为混进 ABI 定义。 + +pub mod envelope; +pub mod tag; +pub mod v1; + +pub use v1::{ + ABI_MAJOR, ABI_MINOR, DriverApiV1, DriverHandle, ENTRY_SYMBOL, FfiEventCallback, + FfiOwnedBuffer, FfiReadItem, FfiSlice, FfiStr, FfiWriteItem, +}; diff --git a/crates/driver-sdk/src/abi/tag.rs b/crates/driver-abi/src/tag.rs similarity index 100% rename from crates/driver-sdk/src/abi/tag.rs rename to crates/driver-abi/src/tag.rs diff --git a/crates/driver-sdk/src/abi.rs b/crates/driver-abi/src/v1.rs similarity index 99% rename from crates/driver-sdk/src/abi.rs rename to crates/driver-abi/src/v1.rs index f606523..663a2af 100644 --- a/crates/driver-sdk/src/abi.rs +++ b/crates/driver-abi/src/v1.rs @@ -17,9 +17,6 @@ //! 谁分配,谁释放:Plugin 返回的 owned buffer 必须通过 `free_buffer` 释放; //! 请求参数默认由 Core 持有,Plugin 只能在调用期内借用。 -pub mod envelope; -pub mod tag; - use std::ffi::c_void; /// ABI 主版本(§18):`Core 1.x 可加载 Plugin 1.0 ~ 1.x,不能加载 2.x`。 diff --git a/crates/driver-contract/Cargo.toml b/crates/driver-contract/Cargo.toml new file mode 100644 index 0000000..52a4ad7 --- /dev/null +++ b/crates/driver-contract/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "driver-contract" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true + +[dependencies] +observation-model = { path = "../observation-model" } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +async-trait = "0.1" +tokio = { version = "1", features = ["sync"] } diff --git a/crates/driver-sdk/src/capabilities.rs b/crates/driver-contract/src/capabilities.rs similarity index 100% rename from crates/driver-sdk/src/capabilities.rs rename to crates/driver-contract/src/capabilities.rs diff --git a/crates/driver-sdk/src/driver.rs b/crates/driver-contract/src/driver.rs similarity index 100% rename from crates/driver-sdk/src/driver.rs rename to crates/driver-contract/src/driver.rs diff --git a/crates/driver-contract/src/error.rs b/crates/driver-contract/src/error.rs new file mode 100644 index 0000000..01934a9 --- /dev/null +++ b/crates/driver-contract/src/error.rs @@ -0,0 +1,129 @@ +//! 调用级错误模型 V2(Runtime V2 方案 §28 Normative)。 +//! +//! 保持现有 `observation_model::DriverErrorInfo` 的公开语义不变;本模块新增 +//! Runtime 内部使用的调用级分类包装 [`DriverCallError`]: +//! +//! - `RawReadResult.error` 等现有逐项错误仍保持 `DriverErrorInfo` 语义; +//! - Host/Session 级失败使用 `DriverCallError`,到 Profile/Domain 映射前 +//! 再落回既有 Quality 规则(方案 §6.1)。 +//! +//! # 北向边界(§28.1) +//! +//! `DriverErrorCategory` 是 Runtime 内部分类,**不得直接成为 REST v1 / MQTT +//! 的新公开枚举**:`DriverCallError.info.code` 经 control-engine 现有稳定码 +//! 白名单映射(未知码归一 `driver_error`);MVP 的 `HostUnavailable` 不新增 +//! REST code——调用级失败复用 `driver_call_failed`,连接 CircuitOpen 复用 +//! `connection_failed`。 + +use serde::{Deserialize, Serialize}; + +/// Driver 调用失败的 Runtime 内部分类(§28)。 +/// +/// 跨 C ABI 时由 `driver-abi` 层映射为稳定 `u32` category code; +/// 禁止把 Rust enum 默认布局直接暴露给插件(§28)。 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DriverErrorCategory { + /// 配置非法(连接参数等)。 + Config, + /// 地址无法被 Driver 解析或校验失败。 + InvalidAddress, + /// 能力未声明(capability 为 false 时的标准拒绝,§15)。 + Unsupported, + + /// 连接建立失败。 + ConnectionFailed, + /// 已建立连接意外中断。 + ConnectionLost, + /// 调用超时。 + Timeout, + + /// 协议帧/序列违反协议规范。 + ProtocolViolation, + /// 设备显式拒绝(协议异常码等)。 + DeviceRejected, + + /// Driver 实现内部 panic(进程内可观测)。 + DriverPanic, + /// Driver 进程崩溃(Host 场景)。 + DriverCrashed, + /// Driver Host 不可达/不可用。 + HostUnavailable, + + /// 取消(未开始或可证明未生效)。 + Cancelled, + /// 截止时间已过(含队列等待期过期)。 + DeadlineExceeded, +} + +/// Host/Session 级调用错误包装(§28)。 +/// +/// `info` 携带稳定码与消息(北向白名单映射的输入),`category` 供 Runtime +/// 做故障恢复决策与指标分类(内部使用,不北向透传)。 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DriverCallError { + pub info: crate::DriverErrorInfo, + pub category: DriverErrorCategory, +} + +impl DriverCallError { + /// 以稳定码 + 分类构造(message 由调用方补足)。 + pub fn new( + code: impl Into, + message: impl Into, + category: DriverErrorCategory, + ) -> Self { + Self { + info: crate::DriverErrorInfo { + code: code.into(), + message: message.into(), + protocol_code: None, + retryable: false, + }, + category, + } + } +} + +impl std::fmt::Display for DriverCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} (category={:?}): {}", + self.info.code, self.category, self.info.message + ) + } +} + +impl std::error::Error for DriverCallError {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn call_error_round_trips_through_json() { + let err = DriverCallError { + info: crate::DriverErrorInfo { + code: "timeout".to_owned(), + message: "读取超时".to_owned(), + protocol_code: None, + retryable: true, + }, + category: DriverErrorCategory::Timeout, + }; + let json = serde_json::to_string(&err).expect("序列化失败"); + let back: DriverCallError = serde_json::from_str(&json).expect("反序列化失败"); + assert_eq!(err, back); + } + + #[test] + fn category_is_internal_not_northbound() { + // §28.1:分类是 Runtime 内部概念,serde 表示仅供内部日志/metrics 使用; + // 稳定码经 info.code 进入既有白名单,此处固化 snake_case 形状防漂移。 + assert_eq!( + serde_json::to_string(&DriverErrorCategory::HostUnavailable).expect("序列化失败"), + r#""host_unavailable""# + ); + } +} diff --git a/crates/driver-sdk/src/items.rs b/crates/driver-contract/src/items.rs similarity index 100% rename from crates/driver-sdk/src/items.rs rename to crates/driver-contract/src/items.rs diff --git a/crates/driver-contract/src/lib.rs b/crates/driver-contract/src/lib.rs new file mode 100644 index 0000000..ffa410a --- /dev/null +++ b/crates/driver-contract/src/lib.rs @@ -0,0 +1,39 @@ +//! driver-contract:Core、Driver Runtime 与 Driver Host 共用的 Rust Driver 语义契约 +//! (Runtime V2 方案 §6.1 Normative)。 +//! +//! 本 crate 拥有 Driver 调用 payload / result 类型与 Rust `Driver` trait; +//! 不包含 `libloading`、Native C ABI(见 `driver-abi`)、具体协议逻辑和 +//! Tokio 调度类型。Raw/Quality 数据类型仍以 `observation-model` 为唯一事实来源, +//! 此处仅 re-export,不复制定义。 +//! +//! # 依赖方向(§6.1) +//! +//! ```text +//! observation-model +//! ↑ +//! driver-contract +//! ↑ +//! device-manager / driver-runtime / driver-host adapters +//! ``` +//! +//! 本 crate **不依赖** `driver-abi`;跨动态库边界的 C ABI 类型属于 +//! `driver-abi` crate(Runtime V2 方案 §6.2)。 + +pub mod capabilities; +pub mod driver; +pub mod error; +pub mod items; +pub mod results; + +pub use capabilities::ProtocolCapabilities; +pub use driver::Driver; +pub use error::{DriverCallError, DriverErrorCategory}; +pub use items::{DriverCommand, DriverReadItem, DriverWriteItem}; +pub use results::{ + AddressMetadata, DriverBrowseNode, HistoryRequest, RawCommandResult, RawEvent, RawEventKind, + RawHistoryPage, RawWriteResult, SubscriptionId, SubscriptionRequest, +}; + +// 原始结果边界类型由 observation-model 定义(§7),在此转发以方便使用方。 +// Runtime V2 不重写 observation-model,避免同名 Raw 类型出现两份事实来源(§6.1)。 +pub use observation_model::{DataType, DriverErrorInfo, RawFieldValue, RawReadResult, RawValue}; diff --git a/crates/driver-sdk/src/results.rs b/crates/driver-contract/src/results.rs similarity index 100% rename from crates/driver-sdk/src/results.rs rename to crates/driver-contract/src/results.rs diff --git a/crates/driver-sdk/Cargo.toml b/crates/driver-sdk/Cargo.toml index 4b473bf..94e7d9e 100644 --- a/crates/driver-sdk/Cargo.toml +++ b/crates/driver-sdk/Cargo.toml @@ -7,7 +7,9 @@ license.workspace = true [dependencies] observation-model = { path = "../observation-model" } +driver-contract = { path = "../driver-contract" } +driver-abi = { path = "../driver-abi" } serde = { version = "1", features = ["derive"] } serde_json = "1" async-trait = "0.1" -tokio = { version = "1", features = ["sync"] } \ No newline at end of file +tokio = { version = "1", features = ["sync"] } diff --git a/crates/driver-sdk/src/lib.rs b/crates/driver-sdk/src/lib.rs index 824d61f..3af5779 100644 --- a/crates/driver-sdk/src/lib.rs +++ b/crates/driver-sdk/src/lib.rs @@ -1,34 +1,80 @@ //! driver-sdk:Driver 插件开发 SDK(§15、§16、§17 Normative)。 //! -//! 提供两层契约: +//! # Runtime V2 Transitional 兼容外壳(方案 §6.8) //! -//! 1. **内部 Rust Driver 契约**(`driver`):以"原始协议结果"为边界, -//! Core 只传递 `DriverReadItem`,Driver 返回 `RawReadResult` / `RawEvent` -//! 等原始结果,**不生成 `Observation`**(§7.3)。 -//! 2. **稳定 C ABI v1**(`abi`):唯一入口 `forgelink_driver_entry_v1()`、 -//! `DriverApiV1`、`FfiStr` / `FfiSlice` / `FfiReadItem` / `FfiWriteItem` / -//! `FfiOwnedBuffer`,以及 ABI 版本与结构扩展规则(§17.4、§18)。 +//! 类型本体已拆分至新 crate,本 crate 仅做 re-export 以保持既有 +//! `driver_sdk::*` 导入路径编译不变: //! -//! 禁止跨 FFI 暴露 Rust trait 或 `async fn`;Native Plugin 仅暴露 `cdylib` 入口。 - -pub mod abi; -pub mod capabilities; -pub mod driver; -pub mod items; -pub mod manifest; -pub mod results; - -pub use capabilities::ProtocolCapabilities; -pub use driver::Driver; -pub use items::{DriverCommand, DriverReadItem, DriverWriteItem}; -pub use manifest::DriverManifest; -pub use results::{ - AddressMetadata, DriverBrowseNode, HistoryRequest, RawCommandResult, RawEvent, RawEventKind, - RawHistoryPage, RawWriteResult, SubscriptionId, SubscriptionRequest, +//! - Rust 语义契约(items/results/capabilities/`Driver` trait + 调用级错误模型) +//! → [`driver_contract`]; +//! - 稳定 C ABI v1 类型(FfiStr/FfiSlice/FfiOwnedBuffer、DriverHandle、 +//! DriverApiV1、tag/envelope)→ [`driver_abi`]; +//! - Manifest v1 与 `platform` 常量为 legacy 模块,保留至 Phase 3 由 +//! `driver-package` Manifest v2 取代。 +//! +//! 当前 `driver_sdk::Driver` async Rust trait 为 `Transitional`:可继续服务 +//! 现有 Rust 内部测试/适配,但**不是新的跨插件契约**。目标态 Core 不保存 +//! `Box`;Core 只面向 DeviceHandle/HostClient,Native 插件只面向 +//! `driver-abi`(§6.8)。最后一个 ABI v1 / legacy manifest / legacy Driver +//! trait 使用点迁移完成后删除本兼容外壳。 + +pub mod abi { + //! 稳定 C ABI v1(转发 [`driver_abi`],§16~§18)。 + pub use driver_abi::*; +} + +pub mod capabilities { + //! 协议层能力声明(§13.1;转发 [`driver_contract`])。 + pub use driver_contract::capabilities::*; +} + +pub mod driver { + //! Driver Rust 契约(§15;转发 [`driver_contract`])。 + pub use driver_contract::driver::*; +} + +pub mod items { + //! Driver 请求项类型(§10、§15;转发 [`driver_contract`])。 + pub use driver_contract::items::*; +} + +pub mod manifest { + //! Driver Manifest v1(§20,legacy——Phase 3 由 `driver-package` + //! Manifest v2 取代)。 + pub use crate::manifest_v1::*; +} + +pub mod results { + //! Driver 结果与订阅/历史类型(§15;转发 [`driver_contract`])。 + pub use driver_contract::results::*; +} + +pub mod error { + //! 调用级错误模型 V2(§28;转发 [`driver_contract`])。 + pub use driver_contract::error::*; +} + +pub use driver_abi::{ + DriverApiV1, DriverHandle, FfiEventCallback, FfiOwnedBuffer, FfiReadItem, FfiSlice, FfiStr, + FfiWriteItem, }; +pub use driver_contract::{ + AddressMetadata, Driver, DriverBrowseNode, DriverCallError, DriverCommand, DriverErrorCategory, + DriverReadItem, DriverWriteItem, HistoryRequest, ProtocolCapabilities, RawCommandResult, + RawEvent, RawEventKind, RawHistoryPage, RawWriteResult, SubscriptionId, SubscriptionRequest, +}; + +// Manifest v1(legacy,§6.8)——保持既有 `driver_sdk::DriverManifest` 路径。 +pub use crate::manifest_v1::{AbiVersion, DriverManifest, platform}; + +// ABI 版本与入口符号常量(§17.4、§18)。 +pub use driver_abi::{ABI_MAJOR, ABI_MINOR, ENTRY_SYMBOL}; // 原始结果边界类型由 observation-model 定义(§7),在此转发以方便 Driver 使用。 -pub use observation_model::{DriverErrorInfo, RawFieldValue, RawReadResult, RawValue}; +pub use observation_model::{DataType, DriverErrorInfo, RawFieldValue, RawReadResult, RawValue}; + +// Manifest v1 本体暂留于此(legacy 模块,§6.8);见上方 manifest 模块文档。 +mod manifest_v1; #[cfg(test)] mod tests { @@ -41,6 +87,8 @@ mod tests { assert_eq!(abi::ABI_MAJOR, 1); assert_eq!(abi::ABI_MINOR, 0); assert_eq!(abi::ENTRY_SYMBOL, "forgelink_driver_entry_v1"); + // re-export 与本体一致。 + assert_eq!(ABI_MAJOR, abi::ABI_MAJOR); } #[test] @@ -63,8 +111,8 @@ mod tests { "platforms": ["windows-x86_64"] }"#; let back: DriverManifest = serde_json::from_str(json).expect("反序列化失败"); - assert_eq!(back.entry, abi::ENTRY_SYMBOL); - assert_eq!(back.abi.major, abi::ABI_MAJOR); + assert_eq!(back.entry, ENTRY_SYMBOL); + assert_eq!(back.abi.major, ABI_MAJOR); } #[test] @@ -79,10 +127,25 @@ mod tests { let item = DriverReadItem { id: 1, address: "1!40001".to_owned(), - expected_type: Some(observation_model::DataType::U16), + expected_type: Some(DataType::U16), }; let json = serde_json::to_string(&item).expect("序列化失败"); let back: DriverReadItem = serde_json::from_str(&json).expect("反序列化失败"); assert_eq!(item, back); } + + #[test] + fn contract_and_reexports_are_same_types() { + // 外壳 re-export 的类型与新 crate 本体必须是同一类型(而非复制定义): + // 同一实例可双向赋值即证明。 + let caps: driver_contract::ProtocolCapabilities = ProtocolCapabilities::default(); + let _: ProtocolCapabilities = caps; + let item = driver_contract::DriverReadItem { + id: 1, + address: "a".to_owned(), + expected_type: None, + }; + let _: DriverReadItem = item; + let _: driver_abi::FfiStr = FfiStr::empty(); + } } diff --git a/crates/driver-sdk/src/manifest_v1.rs b/crates/driver-sdk/src/manifest_v1.rs new file mode 100644 index 0000000..0d3f480 --- /dev/null +++ b/crates/driver-sdk/src/manifest_v1.rs @@ -0,0 +1,61 @@ +//! Driver Manifest v1(§20,legacy)。 +//! +//! Runtime V2 Phase 3 将由 `driver-package` 的 Manifest v2 取代; +//! 在此之前四个现有 Driver 与 driver-loader 继续经由 `driver_sdk::manifest` +//! / `driver_sdk::DriverManifest` 使用本定义(方案 §6.8 Transitional)。 + +use serde::{Deserialize, Serialize}; + +use crate::abi::{ABI_MAJOR, ABI_MINOR}; + +/// 目标平台标识常量(§20、§29)。 +/// +/// 值格式与 Manifest `platforms` 字段一致。 +pub mod platform { + /// Windows x64。 + pub const WINDOWS_X86_64: &str = "windows-x86_64"; + /// Linux x64。 + pub const LINUX_X86_64: &str = "linux-x86_64"; + /// Linux ARM64。 + pub const LINUX_AARCH64: &str = "linux-aarch64"; +} + +/// Manifest 声明的 ABI 版本(§20)。 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub struct AbiVersion { + pub major: u16, + pub minor: u16, +} + +impl Default for AbiVersion { + /// 默认值与 SDK 当前 ABI 一致(§18:首版 1.0)。 + fn default() -> Self { + Self { + major: ABI_MAJOR, + minor: ABI_MINOR, + } + } +} + +/// Driver Manifest(§20)。 +/// +/// Loader 必须同时验证(§20):manifest ABI、entry symbol、 +/// `DriverApiV1.abi_major/minor`、`struct_size`、required feature flags; +/// Manifest 声明与实际入口不一致时拒绝加载。 +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct DriverManifest { + /// 协议驱动 ID(如 `modbus-tcp`)。 + pub id: String, + pub name: String, + pub version: String, + /// 入口符号名(§16、§18)。 + #[serde(default = "default_entry")] + pub entry: String, + pub abi: AbiVersion, + /// 目标平台列表(值见 `platform` 模块常量)。 + pub platforms: Vec, +} + +fn default_entry() -> String { + crate::abi::ENTRY_SYMBOL.to_owned() +} diff --git a/docs/architecture/runtime-v2.md b/docs/architecture/runtime-v2.md index fe89b8a..c4d15e6 100644 --- a/docs/architecture/runtime-v2.md +++ b/docs/architecture/runtime-v2.md @@ -97,8 +97,8 @@ Phase 2~7 仅允许 `legacy-native-runtime` 迁移路径直接加载 ABI v1; | PR | 内容(方案 §38) | 对应 Phase | 状态 | |---|---|---|---| | PR-0 | clippy 存量清零(§39 门禁前置条件) | — | ✅ #32 已合并(2026-08-25) | -| PR-1 | Runtime V2 RFC / terminology(本文件) | 0 | 🔄 进行中 | -| PR-2 | `driver-contract` + `driver-abi` split + `driver-sdk` facade | 1 | ⬜ 未开始 | +| PR-1 | Runtime V2 RFC / terminology(本文件) | 0 | ✅ #33 已合并(2026-08-25) | +| PR-2 | `driver-contract` + `driver-abi` split + `driver-sdk` facade | 1 | 🔄 本 PR | | PR-3 | `driver-package` + Manifest v2 + 四包迁移 | 1 | ⬜ 未开始 | | PR-4 | Multi Driver Registry | 2 | ⬜ 未开始 | | PR-5 | Startup Preflight | 3 | ⬜ 未开始 |