Skip to main content

rs_teststand/sequence/
run_mode.rs

1//! How a step behaves when its sequence runs.
2
3/// What the engine does with a step when it reaches it (`RunModes`).
4///
5/// The engine exchanges this as a string, not a number, so a caller passing an
6/// integer is silently setting something else. That is the whole reason this
7/// type exists.
8///
9/// ```
10/// use rs_teststand::RunMode;
11///
12/// assert_eq!(RunMode::ForcePass.as_str(), "Pass");
13/// assert_eq!(RunMode::from_value("Skip"), Some(RunMode::Skip));
14/// ```
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
16#[non_exhaustive]
17pub enum RunMode {
18    /// Run the step and report what happened (`RunMode_Normal`).
19    #[default]
20    Normal,
21    /// Do not run the step (`RunMode_Skip`).
22    Skip,
23    /// Do not run the step; report it as passed (`RunMode_ForcePass`).
24    ForcePass,
25    /// Do not run the step; report it as failed (`RunMode_ForceFail`).
26    ForceFail,
27}
28
29impl RunMode {
30    /// The value the engine expects.
31    #[must_use]
32    pub const fn as_str(self) -> &'static str {
33        match self {
34            Self::Normal => "Normal",
35            Self::Skip => "Skip",
36            Self::ForcePass => "Pass",
37            Self::ForceFail => "Fail",
38        }
39    }
40
41    /// Recognizes a run mode read back from a step.
42    ///
43    /// `None` means a value this build does not name rather than a failure.
44    #[must_use]
45    pub fn from_value(value: &str) -> Option<Self> {
46        [Self::Normal, Self::Skip, Self::ForcePass, Self::ForceFail]
47            .into_iter()
48            .find(|candidate| candidate.as_str() == value)
49    }
50}
51
52impl std::fmt::Display for RunMode {
53    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        formatter.write_str(self.as_str())
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::RunMode;
61
62    #[test]
63    fn the_forcing_modes_do_not_spell_themselves_the_way_they_are_named() {
64        // The trap: the names say ForcePass and ForceFail, the engine says Pass
65        // and Fail. Deriving the string from the variant name would be wrong.
66        assert_eq!(RunMode::ForcePass.as_str(), "Pass");
67        assert_eq!(RunMode::ForceFail.as_str(), "Fail");
68    }
69
70    #[test]
71    fn every_mode_round_trips() {
72        for mode in [
73            RunMode::Normal,
74            RunMode::Skip,
75            RunMode::ForcePass,
76            RunMode::ForceFail,
77        ] {
78            assert_eq!(RunMode::from_value(mode.as_str()), Some(mode));
79        }
80    }
81
82    #[test]
83    fn a_step_runs_unless_told_otherwise() {
84        assert_eq!(RunMode::default(), RunMode::Normal);
85    }
86
87    #[test]
88    fn an_unrecognized_mode_is_reported_rather_than_guessed() {
89        // A caller that passed a number would land here, not on a valid mode.
90        assert_eq!(RunMode::from_value("0"), None);
91        assert_eq!(RunMode::from_value("normal"), None);
92    }
93}