1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use stepflow_base::{generate_id_type, IdError, ObjectStoreContent};
use stepflow_data::{StateData, var::VarId};

generate_id_type!(StepId);

#[derive(Debug)]
/// A single step in a flow
///
/// A step is defined by its the required inputs to enter the step and the outputs it must fulfill to exit the step.
/// Substeps allow for grouping of steps and are executing in order by default.
pub struct Step {
  pub id: StepId,
  pub input_vars: Option<Vec<VarId>>,
  pub output_vars: Vec<VarId>,

  substep_step_ids: Option<Vec<StepId>>,
}

impl ObjectStoreContent for Step {
    type IdType = StepId;

    fn new_id(id_val: u16) -> Self::IdType {
      StepId::new(id_val)
    }

    fn id(&self) -> &Self::IdType {
      &self.id
    }
}

impl Step {
  /// Create a new step.
  ///
  /// If no inputs are required, pass in `None` for `input_vars`
  pub fn new(id: StepId, input_vars: Option<Vec<VarId>>, output_vars: Vec<VarId>) -> Self {
    Step {
      id,
      input_vars,
      output_vars,
      substep_step_ids: None,
    }
  }

  #[cfg(test)]
  pub fn test_new() -> Self {
    Step::new(stepflow_test_util::test_id!(StepId), None, vec![])
  }

  pub fn get_input_vars(&self) -> &Option<Vec<VarId>> {
    &self.input_vars
  }

  pub fn get_output_vars(&self) -> &Vec<VarId> {
    &self.output_vars
  }

  /// Push a substep to the end of the current sub-steps
  pub fn push_substep(&mut self, substep_step_id: StepId) {
    match &mut self.substep_step_ids {
      None => self.substep_step_ids = Some(vec![substep_step_id]),
      Some(substep_step_ids) => substep_step_ids.push(substep_step_id),
    }
  }

  /// Get the sub-step that directly follows `prev_substep_id`
  pub fn next_substep(&self, prev_substep_id: &StepId) -> Option<&StepId> {
    let mut skipped = false;
    let mut iter = self.substep_step_ids
      .as_ref()?
      .iter()
      .skip_while(|step_id| {
        // find the prev, let it skip once, then stop
        if skipped { 
          return false;
        }
        if *step_id == prev_substep_id {
          skipped = true;
        }
        true
      });
    iter.next()
  }

  pub fn first_substep(&self) -> Option<&StepId> {
    self.substep_step_ids.as_ref()?.first()
  }

  /// Verifies that `inputs` fulfills the required inputs to enter the step
  pub fn can_enter(&self, inputs: &StateData) -> Result<(), IdError<VarId>> {
    // see if we're missing any inputs
    if let Some(input_vars) = &self.input_vars {
      let first_missing_input = input_vars.iter().find(|input_var_id| !inputs.contains(input_var_id));
      if first_missing_input.is_some() {
        return Err(IdError::IdMissing(first_missing_input.unwrap().clone()))
      }
    }

    Ok(())
  }

  /// Verifies that `state_data` fulfills the required outputs to exit the step
  pub fn can_exit(&self, state_data: &StateData) -> Result<(), IdError<VarId>> {
    // see if we're missing any inputs
    self.can_enter(state_data)?;

    // see if we're missing any outputs
    let first_missing_output = &self.output_vars.iter().find(|output_var_id| !state_data.contains(output_var_id));
    if first_missing_output.is_some() {
      return Err(IdError::IdMissing(first_missing_output.unwrap().clone()))
    }

    Ok(())
  }
}

#[cfg(test)]
mod tests {
  use stepflow_base::ObjectStoreContent;
  use super::{ Step };

  #[test]
  fn test_add_get_substep() {
    // no substep
    let mut step = Step::test_new();
    assert_eq!(step.first_substep(), None);

    // add one
    let substep1 = Step::test_new();
    step.push_substep(substep1.id().clone());
    assert_eq!(step.first_substep().unwrap(), substep1.id());
    assert_eq!(step.next_substep(&substep1.id()), None);

    // add another
    let substep2 = Step::test_new();
    step.push_substep(substep2.id().clone());
    assert_eq!(step.first_substep().unwrap(), substep1.id());
    assert_eq!(step.next_substep(substep1.id()).unwrap(), substep2.id());
    assert_eq!(step.next_substep(&substep2.id()), None);
  }
}