IDataTypeProvider 数据类型扩展

Start 节点的参数/变量需要选择「类型」。框架内置了 String/Integer/Float/Boolean/DateTime/Dynamic 六种,但真实业务里常有领域类型:`User`、`Order`、`Document`……这些结构化类型由谁定义?`IDataTypeProvider` 把这个口子交给调用方。

类型分类

`DataTypeCategory` 把所有类型归为三类,决定编辑形态:

pub enum DataTypeCategory {
    Basic,    // 基础标量,直接 value
    Complex,  // provider 预定义结构,结构不可编辑
    Dynamic,  // 结构可手动增删改
}

| 分类 | 结构来源 | 结构可编辑 | 典型类型 | |------|----------|-----------|----------| | Basic | 无字段 | 否 | String/Integer/Float/Boolean/DateTime | | Complex | provider | 否(只读) | User、Order 等领域类型 | | Dynamic | 用户运行时定义 | 是 | Dynamic(自定义结构) |

Basic 与 Dynamic 是内置的,Complex 必须由 provider 提供——这正是 `IDataTypeProvider` 的用武之地。

两个 trait:类型与提供者

接口分两层:单个类型实现 `IDataType`,提供者实现 `IDataTypeProvider` 汇总一批类型:

pub trait IDataType: Send + Sync {
    fn name(&self) -> &str;                       // 唯一标识,如 "User"
    fn category(&self) -> DataTypeCategory;       // Basic/Complex/Dynamic
    fn fields(&self) -> Vec<DataTypeField>;       // 子字段(Basic 返回空)
}

pub trait IDataTypeProvider: Send + Sync {
    fn data_types(&self) -> Vec<Box<dyn IDataType>>;
}

pub type SharedDataTypeProvider = Arc<dyn IDataTypeProvider>;

`DataTypeField` 描述子字段:

pub struct DataTypeField {
    pub name: String,            // 字段名
    pub field_type: String,      // 类型名(引用注册表中的类型,如 "Integer")
    pub default_value: String,   // 默认值
}

注意 `field_type` 是**字符串引用**而非枚举——子字段类型可以是注册表里的任意类型(包括另一个 Complex),支持类型组合。

DataTypeRegistry:合并内置与 provider

`DataTypeRegistry` 是类型的运行时查询入口,构造时合并内置类型与 provider 类型:

impl DataTypeRegistry {
    pub fn new(provider: Option<SharedDataTypeProvider>) -> Self {
        let mut types: Vec<Box<dyn IDataType>> = vec![
            Box::new(BuiltinBasicType::new("String")),
            Box::new(BuiltinBasicType::new("Integer")),
            Box::new(BuiltinBasicType::new("Float")),
            Box::new(BuiltinBasicType::new("Boolean")),
            Box::new(BuiltinBasicType::new("DateTime")),
            Box::new(DynamicType),    // Dynamic 始终内置
        ];
        if let Some(p) = provider {
            types.extend(p.data_types());   // 追加 provider 类型
        }
        Self { types }
    }
}

关键设计:

- 内置类型**始终可用**,不注入 provider 也能工作 - provider 类型是**追加**而非替换,不会覆盖内置 - `Dynamic` 永远内置,作为「自定义结构」的兜底出口

注册表提供一组查询方法,面板据此决定编辑形态:

| 方法 | 用途 | |------|------| | `type_names()` | 类型下拉菜单选项 | | `get(name)` | 按名查类型 | | `is_basic/is_complex/is_dynamic(name)` | 判断分类 | | `has_fields(name)` | 是否有子字段 | | `fields(name)` | 取子字段定义 | | `is_structure_editable(name)` | 结构是否可编辑(仅 Dynamic) |

注入:替换型 + 销毁重建

与 `ToolbarProvider` 的累积注入不同,`set_data_type_provider` 是**替换型**:

pub fn set_data_type_provider(&mut self, provider: SharedDataTypeProvider, cx) {
    self.data_type_provider = Some(provider);
    self.panel_view = None;   // 销毁现有面板
    cx.notify();
}

为什么必须销毁 panel_view?因为已有面板持有旧 `DataTypeRegistry` 的引用,类型下拉、子字段渲染都基于旧类型集。若不重建,用户会看到「注册表已变、面板还显示旧类型」的撕裂状态。销毁后下一帧 `ensure_panel_view` 用新 provider 重建 `StartPanelView`,其 `build` 会用新 `DataTypeRegistry` 重新构建所有 `ItemState`。

这是替换型扩展点的统一语义:**注入即重建依赖它的视图**。本章的 SyntaxService 与下一节的 Language 都遵循此规则。

实战:自定义复杂类型

假设业务有 `User` 类型,含 id/name/email 三个字段:

use rust_agent_flow_gpui::{
    DataTypeCategory, DataTypeField, IDataType, IDataTypeProvider, SharedDataTypeProvider,
};

struct UserType;
impl IDataType for UserType {
    fn name(&self) -> &str { "User" }
    fn category(&self) -> DataTypeCategory { DataTypeCategory::Complex }
    fn fields(&self) -> Vec<DataTypeField> {
        vec![
            DataTypeField::new("id",    "Integer", "0"),
            DataTypeField::new("name",  "String",  ""),
            DataTypeField::new("email", "String",  ""),
        ]
    }
}

struct OrderType;
impl IDataType for OrderType {
    fn name(&self) -> &str { "Order" }
    fn category(&self) -> DataTypeCategory { DataTypeCategory::Complex }
    fn fields(&self) -> Vec<DataTypeField> {
        vec![
            DataTypeField::new("order_id", "String", ""),
            DataTypeField::new("user",     "User",   ""),  // 引用另一个 Complex
            DataTypeField::new("amount",   "Float",  "0"),
        ]
    }
}

pub struct DomainDataTypeProvider;
impl IDataTypeProvider for DomainDataTypeProvider {
    fn data_types(&self) -> Vec<Box<dyn IDataType>> {
        vec![Box::new(UserType), Box::new(OrderType)]
    }
}

注入:

editor.set_data_type_provider(Arc::new(DomainDataTypeProvider), cx);

之后 Start 面板的类型下拉里就多了 `User`、`Order`,选中后展开只读子字段。`Order.user` 引用 `User` 体现了 `field_type` 字符串引用的威力——类型可任意组合嵌套。

Demo 的空 provider

Demo 的 `DemoDataTypeProvider` 当前返回空 vec,仅作扩展点参考:

pub struct DemoDataTypeProvider;
impl IDataTypeProvider for DemoDataTypeProvider {
    fn data_types(&self) -> Vec<Box<dyn IDataType>> { vec![] }
}

Demo 数据均用内置类型,故无需注入。真实项目按上节示例填充即可。

小结

`IDataType`/`IDataTypeProvider` 两层接口让调用方注入领域 Complex 类型;`DataTypeRegistry` 合并内置与 provider 类型,内置始终可用;`set_data_type_provider` 是替换型注入,销毁 panel_view 以避免新旧类型集撕裂。`field_type` 字符串引用支持类型嵌套组合。

下一节:[SyntaxService 语法高亮扩展](syntax-service.md)