1use stepflow_base::{generate_id_type, IdError, ObjectStoreContent};
2use stepflow_data::{StateData, var::VarId};
3
4generate_id_type!(StepId);
5
6#[derive(Debug)]
7pub struct Step {
12 pub id: StepId,
13 pub input_vars: Option<Vec<VarId>>,
14 pub output_vars: Vec<VarId>,
15
16 substep_step_ids: Option<Vec<StepId>>,
17}
18
19impl ObjectStoreContent for Step {
20 type IdType = StepId;
21
22 fn new_id(id_val: u16) -> Self::IdType {
23 StepId::new(id_val)
24 }
25
26 fn id(&self) -> &Self::IdType {
27 &self.id
28 }
29}
30
31impl Step {
32 pub fn new(id: StepId, input_vars: Option<Vec<VarId>>, output_vars: Vec<VarId>) -> Self {
36 Step {
37 id,
38 input_vars,
39 output_vars,
40 substep_step_ids: None,
41 }
42 }
43
44 #[cfg(test)]
45 pub fn test_new() -> Self {
46 Step::new(stepflow_test_util::test_id!(StepId), None, vec![])
47 }
48
49 pub fn get_input_vars(&self) -> &Option<Vec<VarId>> {
50 &self.input_vars
51 }
52
53 pub fn get_output_vars(&self) -> &Vec<VarId> {
54 &self.output_vars
55 }
56
57 pub fn push_substep(&mut self, substep_step_id: StepId) {
59 match &mut self.substep_step_ids {
60 None => self.substep_step_ids = Some(vec![substep_step_id]),
61 Some(substep_step_ids) => substep_step_ids.push(substep_step_id),
62 }
63 }
64
65 pub fn next_substep(&self, prev_substep_id: &StepId) -> Option<&StepId> {
67 let mut skipped = false;
68 let mut iter = self.substep_step_ids
69 .as_ref()?
70 .iter()
71 .skip_while(|step_id| {
72 if skipped {
74 return false;
75 }
76 if *step_id == prev_substep_id {
77 skipped = true;
78 }
79 true
80 });
81 iter.next()
82 }
83
84 pub fn first_substep(&self) -> Option<&StepId> {
85 self.substep_step_ids.as_ref()?.first()
86 }
87
88 pub fn can_enter(&self, inputs: &StateData) -> Result<(), IdError<VarId>> {
90 if let Some(input_vars) = &self.input_vars {
92 let first_missing_input = input_vars.iter().find(|input_var_id| !inputs.contains(input_var_id));
93 if first_missing_input.is_some() {
94 return Err(IdError::IdMissing(first_missing_input.unwrap().clone()))
95 }
96 }
97
98 Ok(())
99 }
100
101 pub fn can_exit(&self, state_data: &StateData) -> Result<(), IdError<VarId>> {
103 self.can_enter(state_data)?;
105
106 let first_missing_output = &self.output_vars.iter().find(|output_var_id| !state_data.contains(output_var_id));
108 if first_missing_output.is_some() {
109 return Err(IdError::IdMissing(first_missing_output.unwrap().clone()))
110 }
111
112 Ok(())
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use stepflow_base::ObjectStoreContent;
119 use super::{ Step };
120
121 #[test]
122 fn test_add_get_substep() {
123 let mut step = Step::test_new();
125 assert_eq!(step.first_substep(), None);
126
127 let substep1 = Step::test_new();
129 step.push_substep(substep1.id().clone());
130 assert_eq!(step.first_substep().unwrap(), substep1.id());
131 assert_eq!(step.next_substep(&substep1.id()), None);
132
133 let substep2 = Step::test_new();
135 step.push_substep(substep2.id().clone());
136 assert_eq!(step.first_substep().unwrap(), substep1.id());
137 assert_eq!(step.next_substep(substep1.id()).unwrap(), substep2.id());
138 assert_eq!(step.next_substep(&substep2.id()), None);
139 }
140}