Skip to main content

sz_rust_workflow/definition/
models.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2024-2026 SZ-Rust Team
3//
4use serde::{Deserialize, Serialize};
5
6use super::node::{Node, NodeEdge};
7use super::strategy::Transition;
8
9/// 定义格式。
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum DefinitionFormat {
13    Yaml,
14    Json,
15}
16
17impl DefinitionFormat {
18    /// 按首字符猜测格式:`{` → JSON,其他 → YAML。
19    pub fn detect(text: &str) -> Self {
20        let trimmed = text.trim_start();
21        if trimmed.starts_with('{') {
22            Self::Json
23        } else {
24            Self::Yaml
25        }
26    }
27}
28
29/// 流程定义,对齐 design 2.3.2 类图。
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31pub struct FlowDefinition {
32    /// 流程标识,小写字母/数字/下划线/点号,长度 1~64
33    pub flow_key: String,
34    /// 语义化版本
35    pub version: semver::Version,
36    /// 流程名称,长度 1~128
37    pub name: String,
38    /// 节点集合
39    pub nodes: Vec<Node>,
40    /// 起始节点 ID
41    pub start_node: String,
42    /// 是否为生效版本
43    #[serde(default)]
44    pub active: bool,
45    /// 是否已弃用
46    #[serde(default)]
47    pub deprecated: bool,
48    /// 状态机定义(可选,纯状态机流程)
49    #[serde(default, skip_serializing_if = "Option::is_none")]
50    pub machine: Option<StateMachineDefinition>,
51    /// 审批流定义(可选,纯审批流)
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub flow: Option<ApprovalFlowDefinition>,
54}
55
56/// 状态机定义。
57#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
58pub struct StateMachineDefinition {
59    /// 初始状态
60    pub initial_state: String,
61    /// 所有状态集合
62    pub states: Vec<String>,
63    /// 迁移规则
64    pub transitions: Vec<Transition>,
65}
66
67/// 审批流定义。
68#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
69pub struct ApprovalFlowDefinition {
70    /// 节点边集合(描述节点间连接关系)
71    pub edges: Vec<NodeEdge>,
72}
73
74impl FlowDefinition {
75    /// 按 node_id 查找节点。
76    pub fn find_node(&self, node_id: &str) -> Option<&Node> {
77        self.nodes.iter().find(|n| n.node_id == node_id)
78    }
79
80    /// 获取所有 node_id 集合。
81    pub fn node_ids(&self) -> Vec<&str> {
82        self.nodes.iter().map(|n| n.node_id.as_str()).collect()
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::super::node::{NodeConfig, NodeType};
89    use super::*;
90
91    fn sample_definition() -> FlowDefinition {
92        FlowDefinition {
93            flow_key: "leave_request".into(),
94            version: semver::Version::new(1, 0, 0),
95            name: "请假申请".into(),
96            nodes: vec![
97                Node {
98                    node_id: "start".into(),
99                    node_type: NodeType::Start,
100                    config: NodeConfig::Start { next: "end".into() },
101                },
102                Node {
103                    node_id: "end".into(),
104                    node_type: NodeType::End,
105                    config: NodeConfig::End,
106                },
107            ],
108            start_node: "start".into(),
109            active: true,
110            deprecated: false,
111            machine: None,
112            flow: None,
113        }
114    }
115
116    #[test]
117    fn flow_definition_serde() {
118        let def = sample_definition();
119        let json = serde_json::to_string(&def).unwrap();
120        let back: FlowDefinition = serde_json::from_str(&json).unwrap();
121        assert_eq!(def, back);
122    }
123
124    #[test]
125    fn flow_definition_yaml_serde() {
126        let def = sample_definition();
127        let yaml = serde_yaml::to_string(&def).unwrap();
128        let back: FlowDefinition = serde_yaml::from_str(&yaml).unwrap();
129        assert_eq!(def, back);
130    }
131
132    #[test]
133    fn format_detect() {
134        assert_eq!(
135            DefinitionFormat::detect("{ \"flow_key\": \"x\" }"),
136            DefinitionFormat::Json
137        );
138        assert_eq!(
139            DefinitionFormat::detect("flow_key: x"),
140            DefinitionFormat::Yaml
141        );
142        assert_eq!(DefinitionFormat::detect("  {"), DefinitionFormat::Json);
143    }
144
145    #[test]
146    fn find_node() {
147        let def = sample_definition();
148        assert!(def.find_node("start").is_some());
149        assert!(def.find_node("nonexistent").is_none());
150    }
151
152    #[test]
153    fn node_ids() {
154        let def = sample_definition();
155        assert_eq!(def.node_ids(), vec!["start", "end"]);
156    }
157
158    #[test]
159    fn state_machine_definition_serde() {
160        let sm = StateMachineDefinition {
161            initial_state: "draft".into(),
162            states: vec!["draft".into(), "review".into(), "approved".into()],
163            transitions: vec![Transition {
164                from: "draft".into(),
165                to: "review".into(),
166                event: "submit".into(),
167                guard: None,
168            }],
169        };
170        let json = serde_json::to_string(&sm).unwrap();
171        let back: StateMachineDefinition = serde_json::from_str(&json).unwrap();
172        assert_eq!(sm, back);
173    }
174
175    #[test]
176    fn approval_flow_definition_serde() {
177        let af = ApprovalFlowDefinition {
178            edges: vec![NodeEdge {
179                from: "start".into(),
180                to: "approve".into(),
181                condition: None,
182            }],
183        };
184        let json = serde_json::to_string(&af).unwrap();
185        let back: ApprovalFlowDefinition = serde_json::from_str(&json).unwrap();
186        assert_eq!(af, back);
187    }
188
189    #[test]
190    fn full_definition_with_machine_and_flow() {
191        let mut def = sample_definition();
192        def.machine = Some(StateMachineDefinition {
193            initial_state: "draft".into(),
194            states: vec!["draft".into(), "done".into()],
195            transitions: vec![],
196        });
197        def.flow = Some(ApprovalFlowDefinition { edges: vec![] });
198        let json = serde_json::to_string(&def).unwrap();
199        let back: FlowDefinition = serde_json::from_str(&json).unwrap();
200        assert_eq!(def, back);
201    }
202}