BUG-260621-01: 前端推进链传空 dag {} 时,DagDef.nodes/edges 无 serde
default 致 Tauri IPC 入口报 missing field nodes,挡在 workflow.rs:91
template_for 选模板逻辑之前。加 #[serde(default)] 后 {} 反序列化成空
DagDef(HashMap/Vec Default 均空集合)→ is_empty() 进选模板分支。非空 dag
零回归。2 单测防回归,df-workflow test 51 passed。
114 lines
3.5 KiB
Rust
114 lines
3.5 KiB
Rust
//! 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<String, NodeDef>,
|
|
#[serde(default)]
|
|
pub edges: Vec<EdgeDef>,
|
|
}
|
|
|
|
/// 节点定义
|
|
#[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<String>,
|
|
}
|
|
|
|
/// 边定义
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
pub struct EdgeDef {
|
|
pub source: String,
|
|
pub target: String,
|
|
#[serde(default)]
|
|
pub condition: Option<String>,
|
|
}
|
|
|
|
impl DagDef {
|
|
/// 创建空的 DAG 定义
|
|
pub fn new() -> Self {
|
|
Self {
|
|
nodes: HashMap::new(),
|
|
edges: Vec::new(),
|
|
}
|
|
}
|
|
|
|
/// 添加节点定义
|
|
pub fn add_node(&mut self, id: impl Into<String>, node_type: impl Into<String>, 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<String>, target: impl Into<String>) {
|
|
self.edges.push(EdgeDef {
|
|
source: source.into(),
|
|
target: target.into(),
|
|
condition: None,
|
|
});
|
|
}
|
|
|
|
/// 添加带条件的边定义
|
|
pub fn add_edge_with_condition(
|
|
&mut self,
|
|
source: impl Into<String>,
|
|
target: impl Into<String>,
|
|
condition: impl Into<String>,
|
|
) {
|
|
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());
|
|
}
|
|
}
|