forge_core/workflow/
traits.rs1use std::future::Future;
2use std::pin::Pin;
3use std::str::FromStr;
4use std::time::Duration;
5
6use serde::{Serialize, de::DeserializeOwned};
7
8use super::context::WorkflowContext;
9use crate::Result;
10
11pub trait ForgeWorkflow: Send + Sync + 'static {
13 type Input: DeserializeOwned + Serialize + Send + Sync;
15 type Output: Serialize + Send;
17
18 fn info() -> WorkflowInfo;
20
21 fn execute(
23 ctx: &WorkflowContext,
24 input: Self::Input,
25 ) -> Pin<Box<dyn Future<Output = Result<Self::Output>> + Send + '_>>;
26}
27
28#[derive(Debug, Clone)]
30pub struct WorkflowInfo {
31 pub name: &'static str,
33 pub version: u32,
35 pub timeout: Duration,
37 pub deprecated: bool,
39}
40
41impl Default for WorkflowInfo {
42 fn default() -> Self {
43 Self {
44 name: "",
45 version: 1,
46 timeout: Duration::from_secs(86400), deprecated: false,
48 }
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum WorkflowStatus {
55 Created,
57 Running,
59 Waiting,
61 Completed,
63 Compensating,
65 Compensated,
67 Failed,
69}
70
71impl WorkflowStatus {
72 pub fn as_str(&self) -> &'static str {
74 match self {
75 Self::Created => "created",
76 Self::Running => "running",
77 Self::Waiting => "waiting",
78 Self::Completed => "completed",
79 Self::Compensating => "compensating",
80 Self::Compensated => "compensated",
81 Self::Failed => "failed",
82 }
83 }
84
85 pub fn is_terminal(&self) -> bool {
87 matches!(self, Self::Completed | Self::Compensated | Self::Failed)
88 }
89}
90
91impl FromStr for WorkflowStatus {
92 type Err = std::convert::Infallible;
93
94 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
95 Ok(match s {
96 "created" => Self::Created,
97 "running" => Self::Running,
98 "waiting" => Self::Waiting,
99 "completed" => Self::Completed,
100 "compensating" => Self::Compensating,
101 "compensated" => Self::Compensated,
102 "failed" => Self::Failed,
103 _ => Self::Created,
104 })
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_workflow_info_default() {
114 let info = WorkflowInfo::default();
115 assert_eq!(info.name, "");
116 assert_eq!(info.version, 1);
117 assert!(!info.deprecated);
118 }
119
120 #[test]
121 fn test_workflow_status_conversion() {
122 assert_eq!(WorkflowStatus::Running.as_str(), "running");
123 assert_eq!(WorkflowStatus::Completed.as_str(), "completed");
124 assert_eq!(WorkflowStatus::Compensating.as_str(), "compensating");
125
126 assert_eq!(
127 "running".parse::<WorkflowStatus>(),
128 Ok(WorkflowStatus::Running)
129 );
130 assert_eq!(
131 "completed".parse::<WorkflowStatus>(),
132 Ok(WorkflowStatus::Completed)
133 );
134 }
135
136 #[test]
137 fn test_workflow_status_is_terminal() {
138 assert!(!WorkflowStatus::Running.is_terminal());
139 assert!(!WorkflowStatus::Waiting.is_terminal());
140 assert!(WorkflowStatus::Completed.is_terminal());
141 assert!(WorkflowStatus::Failed.is_terminal());
142 assert!(WorkflowStatus::Compensated.is_terminal());
143 }
144}