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
use stepflow_base::{generate_id_type, IdError, ObjectStoreContent};
use stepflow_data::{StateData, VarId};

generate_id_type!(StepId);

#[derive(Debug)]
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: u32) -> Self::IdType {
      StepId::new(id_val)
    }

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

impl Step {
  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
  }

  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),
    }
  }

  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()
  }

  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(())
  }

  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);
  }
}