use std::future::Future;
use std::pin::Pin;
use std::str::FromStr;
use std::time::Duration;
use serde::{de::DeserializeOwned, Serialize};
use super::context::WorkflowContext;
use crate::Result;
pub trait ForgeWorkflow: Send + Sync + 'static {
type Input: DeserializeOwned + Serialize + Send + Sync;
type Output: Serialize + Send;
fn info() -> WorkflowInfo;
fn execute(
ctx: &WorkflowContext,
input: Self::Input,
) -> Pin<Box<dyn Future<Output = Result<Self::Output>> + Send + '_>>;
}
#[derive(Debug, Clone)]
pub struct WorkflowInfo {
pub name: &'static str,
pub version: u32,
pub timeout: Duration,
pub deprecated: bool,
}
impl Default for WorkflowInfo {
fn default() -> Self {
Self {
name: "",
version: 1,
timeout: Duration::from_secs(86400), deprecated: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WorkflowStatus {
Created,
Running,
Waiting,
Completed,
Compensating,
Compensated,
Failed,
}
impl WorkflowStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Created => "created",
Self::Running => "running",
Self::Waiting => "waiting",
Self::Completed => "completed",
Self::Compensating => "compensating",
Self::Compensated => "compensated",
Self::Failed => "failed",
}
}
pub fn is_terminal(&self) -> bool {
matches!(self, Self::Completed | Self::Compensated | Self::Failed)
}
}
impl FromStr for WorkflowStatus {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
"created" => Self::Created,
"running" => Self::Running,
"waiting" => Self::Waiting,
"completed" => Self::Completed,
"compensating" => Self::Compensating,
"compensated" => Self::Compensated,
"failed" => Self::Failed,
_ => Self::Created,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_workflow_info_default() {
let info = WorkflowInfo::default();
assert_eq!(info.name, "");
assert_eq!(info.version, 1);
assert!(!info.deprecated);
}
#[test]
fn test_workflow_status_conversion() {
assert_eq!(WorkflowStatus::Running.as_str(), "running");
assert_eq!(WorkflowStatus::Completed.as_str(), "completed");
assert_eq!(WorkflowStatus::Compensating.as_str(), "compensating");
assert_eq!(
"running".parse::<WorkflowStatus>(),
Ok(WorkflowStatus::Running)
);
assert_eq!(
"completed".parse::<WorkflowStatus>(),
Ok(WorkflowStatus::Completed)
);
}
#[test]
fn test_workflow_status_is_terminal() {
assert!(!WorkflowStatus::Running.is_terminal());
assert!(!WorkflowStatus::Waiting.is_terminal());
assert!(WorkflowStatus::Completed.is_terminal());
assert!(WorkflowStatus::Failed.is_terminal());
assert!(WorkflowStatus::Compensated.is_terminal());
}
}