Skip to main content

ironflow_engine/config/
workflow.rs

1//! Configuration for workflow (sub-workflow) steps.
2
3use ironflow_core::retry::RetryPolicy;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7/// Configuration for invoking a registered workflow as a sub-step.
8///
9/// The engine will look up the handler by [`workflow_name`](WorkflowStepConfig::workflow_name)
10/// and execute it as a child run with its own steps and lifecycle.
11///
12/// # Examples
13///
14/// ```
15/// use ironflow_engine::config::WorkflowStepConfig;
16/// use serde_json::json;
17///
18/// let config = WorkflowStepConfig::new("build", json!({"branch": "main"}));
19/// assert_eq!(config.workflow_name, "build");
20/// ```
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct WorkflowStepConfig {
23    /// Name of the registered workflow handler to invoke.
24    pub workflow_name: String,
25    /// Payload to pass to the child workflow run.
26    pub payload: Value,
27    /// Optional step-level retry policy.
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    pub retry: Option<RetryPolicy>,
30}
31
32impl WorkflowStepConfig {
33    /// Create a new workflow step config.
34    ///
35    /// # Examples
36    ///
37    /// ```
38    /// use ironflow_engine::config::WorkflowStepConfig;
39    /// use serde_json::json;
40    ///
41    /// let config = WorkflowStepConfig::new("deploy", json!({}));
42    /// assert_eq!(config.workflow_name, "deploy");
43    /// ```
44    pub fn new(workflow_name: &str, payload: Value) -> Self {
45        Self {
46            workflow_name: workflow_name.to_string(),
47            payload,
48            retry: None,
49        }
50    }
51
52    /// Set a step-level retry policy.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// use ironflow_core::retry::RetryPolicy;
58    /// use ironflow_engine::config::WorkflowStepConfig;
59    /// use serde_json::json;
60    ///
61    /// let config = WorkflowStepConfig::new("build", json!({}))
62    ///     .retry_policy(RetryPolicy::new(3));
63    /// assert!(config.retry.is_some());
64    /// ```
65    pub fn retry_policy(mut self, policy: RetryPolicy) -> Self {
66        self.retry = Some(policy);
67        self
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use serde_json::json;
75
76    #[test]
77    fn new_sets_fields() {
78        let config = WorkflowStepConfig::new("build", json!({"key": "val"}));
79        assert_eq!(config.workflow_name, "build");
80        assert_eq!(config.payload["key"], "val");
81    }
82
83    #[test]
84    fn serde_roundtrip() {
85        let config = WorkflowStepConfig::new("deploy", json!({"env": "prod"}));
86        let json = serde_json::to_string(&config).unwrap();
87        let back: WorkflowStepConfig = serde_json::from_str(&json).unwrap();
88        assert_eq!(back.workflow_name, "deploy");
89        assert_eq!(back.payload["env"], "prod");
90    }
91
92    #[test]
93    fn a_config_predating_retry_still_deserializes() {
94        let config: WorkflowStepConfig =
95            serde_json::from_str(r#"{"workflow_name":"build","payload":{"key":"val"}}"#)
96                .expect("deserialize");
97        assert!(config.retry.is_none());
98    }
99
100    #[test]
101    fn retry_policy_roundtrip() {
102        let config = WorkflowStepConfig::new("deploy", json!({})).retry_policy(RetryPolicy::new(3));
103        let json = serde_json::to_string(&config).expect("serialize");
104        let back: WorkflowStepConfig = serde_json::from_str(&json).expect("deserialize");
105        assert_eq!(back.retry.as_ref().unwrap().max_retries(), 3);
106    }
107}