//! DAG 定义 — 可序列化/反序列化,用于持久化和模板 use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// DAG 定义 — 可序列化/反序列化,用于持久化和模板 #[derive(Serialize, Deserialize, Clone, Debug)] pub struct DagDef { // BUG-260621-01: nodes/edges 加 #[serde(default)]。 // 前端 run_workflow 推进链传空 dag `{}`(设计意图:空 dag + target_status → // workflow.rs:91 dag.nodes.is_empty() 进 template_for 自动选模板)。原无 default // 时,`{}` 在 Tauri IPC 入口反序列化 DagDef 即报 `missing field nodes`,挡在选模板 // 逻辑之前。加 default 后 `{}` → 空 DagDef(HashMap/Vec 的 Default 均空集合), // 顺利进 :91 选模板分支。非空 dag 零回归(serde default 仅补缺失字段)。 #[serde(default)] pub nodes: HashMap, #[serde(default)] pub edges: Vec, } /// 节点定义 #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NodeDef { pub id: String, pub node_type: String, pub config: serde_json::Value, #[serde(default)] pub label: Option, } /// 边定义 #[derive(Serialize, Deserialize, Clone, Debug)] pub struct EdgeDef { pub source: String, pub target: String, #[serde(default)] pub condition: Option, } impl DagDef { /// 创建空的 DAG 定义 pub fn new() -> Self { Self { nodes: HashMap::new(), edges: Vec::new(), } } /// 添加节点定义 pub fn add_node(&mut self, id: impl Into, node_type: impl Into, config: serde_json::Value) { let id = id.into(); self.nodes.insert(id.clone(), NodeDef { id, node_type: node_type.into(), config, label: None, }); } /// 添加边定义 pub fn add_edge(&mut self, source: impl Into, target: impl Into) { self.edges.push(EdgeDef { source: source.into(), target: target.into(), condition: None, }); } /// 添加带条件的边定义 pub fn add_edge_with_condition( &mut self, source: impl Into, target: impl Into, condition: impl Into, ) { self.edges.push(EdgeDef { source: source.into(), target: target.into(), condition: Some(condition.into()), }); } } impl Default for DagDef { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; // BUG-260621-01: 前端 run_workflow 推进链传空 dag `{}`,serde 必须能反序列化 // 成空 DagDef(非报 missing field),后续 workflow.rs:91 dag.nodes.is_empty() // 进 template_for 选模板。nodes/edges 加 #[serde(default)] 前此用例会失败。 #[test] fn empty_json_deserializes_to_empty_dagdef() { let dag: DagDef = serde_json::from_str("{}").expect("空 JSON 应反序列化为空 DagDef"); assert!(dag.nodes.is_empty()); assert!(dag.edges.is_empty()); } // 零回归:非空 dag 反序列化行为不变(serde default 仅补缺失字段)。 #[test] fn nonempty_dag_deserializes_unchanged() { let json = r#"{"nodes":{"n1":{"id":"n1","node_type":"echo","config":{}}},"edges":[]}"#; let dag: DagDef = serde_json::from_str(json).expect("非空 dag 反序列化"); assert_eq!(dag.nodes.len(), 1); assert!(dag.nodes.contains_key("n1")); assert!(dag.edges.is_empty()); } }