forge_runtime/workflow/
state.rs1use chrono::{DateTime, Utc};
2use forge_core::workflow::{StepStatus, WorkflowStatus};
3use uuid::Uuid;
4
5#[derive(Debug, Clone)]
7pub struct WorkflowRecord {
8 pub id: Uuid,
10 pub workflow_name: String,
12 pub version: u32,
14 pub owner_subject: Option<String>,
16 pub input: serde_json::Value,
18 pub output: Option<serde_json::Value>,
20 pub status: WorkflowStatus,
22 pub current_step: Option<String>,
24 pub step_results: serde_json::Value,
26 pub started_at: DateTime<Utc>,
28 pub completed_at: Option<DateTime<Utc>>,
30 pub error: Option<String>,
32 pub trace_id: Option<String>,
34}
35
36impl WorkflowRecord {
37 pub fn new(
39 workflow_name: impl Into<String>,
40 version: u32,
41 input: serde_json::Value,
42 owner_subject: Option<String>,
43 ) -> Self {
44 Self {
45 id: Uuid::new_v4(),
46 workflow_name: workflow_name.into(),
47 version,
48 owner_subject,
49 input,
50 output: None,
51 status: WorkflowStatus::Created,
52 current_step: None,
53 step_results: serde_json::json!({}),
54 started_at: Utc::now(),
55 completed_at: None,
56 error: None,
57 trace_id: None,
58 }
59 }
60
61 pub fn with_trace_id(mut self, trace_id: impl Into<String>) -> Self {
63 self.trace_id = Some(trace_id.into());
64 self
65 }
66
67 pub fn start(&mut self) {
69 self.status = WorkflowStatus::Running;
70 }
71
72 pub fn complete(&mut self, output: serde_json::Value) {
74 self.status = WorkflowStatus::Completed;
75 self.output = Some(output);
76 self.completed_at = Some(Utc::now());
77 }
78
79 pub fn fail(&mut self, error: impl Into<String>) {
81 self.status = WorkflowStatus::Failed;
82 self.error = Some(error.into());
83 self.completed_at = Some(Utc::now());
84 }
85
86 pub fn compensating(&mut self) {
88 self.status = WorkflowStatus::Compensating;
89 }
90
91 pub fn compensated(&mut self) {
93 self.status = WorkflowStatus::Compensated;
94 self.completed_at = Some(Utc::now());
95 }
96
97 pub fn set_current_step(&mut self, step: impl Into<String>) {
99 self.current_step = Some(step.into());
100 }
101
102 pub fn add_step_result(&mut self, step_name: &str, result: serde_json::Value) {
104 if let Some(obj) = self.step_results.as_object_mut() {
105 obj.insert(step_name.to_string(), result);
106 }
107 }
108}
109
110#[derive(Debug, Clone)]
112pub struct WorkflowStepRecord {
113 pub id: Uuid,
115 pub workflow_run_id: Uuid,
117 pub step_name: String,
119 pub status: StepStatus,
121 pub result: Option<serde_json::Value>,
123 pub error: Option<String>,
125 pub started_at: Option<DateTime<Utc>>,
127 pub completed_at: Option<DateTime<Utc>>,
129}
130
131impl WorkflowStepRecord {
132 pub fn new(workflow_run_id: Uuid, step_name: impl Into<String>) -> Self {
134 Self {
135 id: Uuid::new_v4(),
136 workflow_run_id,
137 step_name: step_name.into(),
138 status: StepStatus::Pending,
139 result: None,
140 error: None,
141 started_at: None,
142 completed_at: None,
143 }
144 }
145
146 pub fn start(&mut self) {
148 self.status = StepStatus::Running;
149 self.started_at = Some(Utc::now());
150 }
151
152 pub fn complete(&mut self, result: serde_json::Value) {
154 self.status = StepStatus::Completed;
155 self.result = Some(result);
156 self.completed_at = Some(Utc::now());
157 }
158
159 pub fn fail(&mut self, error: impl Into<String>) {
161 self.status = StepStatus::Failed;
162 self.error = Some(error.into());
163 self.completed_at = Some(Utc::now());
164 }
165
166 pub fn compensate(&mut self) {
168 self.status = StepStatus::Compensated;
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_workflow_record_creation() {
178 let record = WorkflowRecord::new("test_workflow", 1, serde_json::json!({}), None);
179 assert_eq!(record.workflow_name, "test_workflow");
180 assert_eq!(record.version, 1);
181 assert_eq!(record.status, WorkflowStatus::Created);
182 }
183
184 #[test]
185 fn test_workflow_record_transitions() {
186 let mut record = WorkflowRecord::new("test", 1, serde_json::json!({}), None);
187
188 record.start();
189 assert_eq!(record.status, WorkflowStatus::Running);
190
191 record.complete(serde_json::json!({"result": "ok"}));
192 assert_eq!(record.status, WorkflowStatus::Completed);
193 assert!(record.completed_at.is_some());
194 }
195
196 #[test]
197 fn test_workflow_step_record() {
198 let workflow_id = Uuid::new_v4();
199 let mut step = WorkflowStepRecord::new(workflow_id, "step1");
200
201 assert_eq!(step.step_name, "step1");
202 assert_eq!(step.status, StepStatus::Pending);
203
204 step.start();
205 assert_eq!(step.status, StepStatus::Running);
206
207 step.complete(serde_json::json!({}));
208 assert_eq!(step.status, StepStatus::Completed);
209 }
210}