Skip to main content

rs_teststand/station/
run_time_error.rs

1//! What the engine does when a step reports a run-time error.
2
3/// The station's response to a run-time error (`RTEOption_*`).
4///
5/// [`ShowDialog`](Self::ShowDialog) is the one to watch on an unattended host:
6/// it suspends the execution until an operator answers, which on an
7/// unattended station is an outage rather than a prompt.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[repr(i32)]
10pub enum RunTimeErrorOption {
11    /// Prompt the operator (`RTEOption_ShowDialog`). Blocks until answered.
12    ShowDialog = 0,
13    /// Pass the error to the calling sequence (`RTEOption_Continue`).
14    Continue = 1,
15    /// Carry on as though nothing happened (`RTEOption_Ignore`).
16    Ignore = 2,
17    /// Abort the execution (`RTEOption_Abort`).
18    Abort = 3,
19    /// Re-run the step that failed (`RTEOption_Retry`).
20    Retry = 4,
21}
22
23impl RunTimeErrorOption {
24    /// Every response the engine defines.
25    pub const ALL: [Self; 5] = [
26        Self::ShowDialog,
27        Self::Continue,
28        Self::Ignore,
29        Self::Abort,
30        Self::Retry,
31    ];
32
33    /// The value the COM boundary expects.
34    #[must_use]
35    pub const fn bits(self) -> i32 {
36        self as i32
37    }
38
39    /// Reads a raw value, returning it unchanged when it is one this build does
40    /// not name.
41    ///
42    /// # Errors
43    /// The raw value, when it matches no known option.
44    pub const fn from_bits(raw: i32) -> Result<Self, i32> {
45        Ok(match raw {
46            0 => Self::ShowDialog,
47            1 => Self::Continue,
48            2 => Self::Ignore,
49            3 => Self::Abort,
50            4 => Self::Retry,
51            other => return Err(other),
52        })
53    }
54
55    /// Whether this response is interactive.
56    ///
57    /// The single question an unattended host needs answered.
58    #[must_use]
59    pub const fn is_interactive(self) -> bool {
60        matches!(self, Self::ShowDialog)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::RunTimeErrorOption;
67
68    #[test]
69    fn only_the_dialog_response_blocks() {
70        assert!(RunTimeErrorOption::ShowDialog.is_interactive());
71        for option in [
72            RunTimeErrorOption::Continue,
73            RunTimeErrorOption::Ignore,
74            RunTimeErrorOption::Abort,
75            RunTimeErrorOption::Retry,
76        ] {
77            assert!(!option.is_interactive(), "{option:?}");
78        }
79    }
80
81    #[test]
82    fn every_option_round_trips_through_its_raw_value() {
83        for option in RunTimeErrorOption::ALL {
84            assert_eq!(RunTimeErrorOption::from_bits(option.bits()), Ok(option));
85        }
86    }
87
88    #[test]
89    fn an_unknown_value_is_returned_rather_than_guessed() {
90        // A newer engine may add a response; reporting the number beats
91        // silently mapping it onto an existing one.
92        assert_eq!(RunTimeErrorOption::from_bits(99), Err(99));
93    }
94}