Skip to main content

erio_workflow/
context.rs

1//! Workflow context for passing data between steps.
2
3use std::collections::HashMap;
4
5use crate::step::StepOutput;
6
7/// Shared context that flows through a workflow execution.
8///
9/// Stores outputs from completed steps so downstream steps can access them.
10#[derive(Debug, Clone, Default)]
11pub struct WorkflowContext {
12    outputs: HashMap<String, StepOutput>,
13}
14
15impl WorkflowContext {
16    /// Creates an empty workflow context.
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    /// Returns the output of a completed step, if available.
22    pub fn output(&self, step_id: &str) -> Option<&StepOutput> {
23        self.outputs.get(step_id)
24    }
25
26    /// Returns all step outputs.
27    pub fn step_outputs(&self) -> &HashMap<String, StepOutput> {
28        &self.outputs
29    }
30
31    /// Stores the output of a completed step.
32    pub fn set_output(&mut self, step_id: &str, output: StepOutput) {
33        self.outputs.insert(step_id.into(), output);
34    }
35
36    /// Returns `true` if the step has completed.
37    pub fn is_completed(&self, step_id: &str) -> bool {
38        self.outputs.contains_key(step_id)
39    }
40
41    /// Returns the IDs of all completed steps.
42    pub fn completed_step_ids(&self) -> Vec<&str> {
43        self.outputs.keys().map(String::as_str).collect()
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    // === Construction ===
52
53    #[test]
54    fn new_context_has_no_outputs() {
55        let ctx = WorkflowContext::new();
56        assert!(ctx.step_outputs().is_empty());
57    }
58
59    // === Step Output Storage ===
60
61    #[test]
62    fn stores_step_output() {
63        let mut ctx = WorkflowContext::new();
64        let output = StepOutput::new("hello");
65        ctx.set_output("step_a", output);
66        assert!(ctx.output("step_a").is_some());
67        assert_eq!(ctx.output("step_a").unwrap().value(), "hello");
68    }
69
70    #[test]
71    fn returns_none_for_unknown_step() {
72        let ctx = WorkflowContext::new();
73        assert!(ctx.output("unknown").is_none());
74    }
75
76    #[test]
77    fn stores_multiple_outputs() {
78        let mut ctx = WorkflowContext::new();
79        ctx.set_output("a", StepOutput::new("A result"));
80        ctx.set_output("b", StepOutput::new("B result"));
81        assert_eq!(ctx.step_outputs().len(), 2);
82    }
83
84    // === Completed Steps ===
85
86    #[test]
87    fn tracks_completed_steps() {
88        let mut ctx = WorkflowContext::new();
89        ctx.set_output("a", StepOutput::new("done"));
90        assert!(ctx.is_completed("a"));
91        assert!(!ctx.is_completed("b"));
92    }
93
94    #[test]
95    fn completed_step_ids_returns_all() {
96        let mut ctx = WorkflowContext::new();
97        ctx.set_output("x", StepOutput::new("1"));
98        ctx.set_output("y", StepOutput::new("2"));
99        let mut ids = ctx.completed_step_ids();
100        ids.sort_unstable();
101        assert_eq!(ids, vec!["x", "y"]);
102    }
103}