rs_teststand/sequence/breakpoint.rs
1//! Breakpoints, and the two scopes they can live in.
2//!
3//! A breakpoint belongs to a step, not to a run. Setting one through
4//! [`Step::set_break_on_step`](crate::Step::set_break_on_step) writes it into
5//! the step, so every execution of that sequence stops there and the setting
6//! outlives the run that set it.
7//!
8//! Passing an execution instead scopes the breakpoint to that one run. The
9//! step on disk is untouched, and the breakpoint goes away with the execution.
10//! That is what a host debugging on behalf of a remote panel wants: a
11//! debugging session should not quietly edit the station's sequence files.
12//!
13//! Nothing here stops a run on its own. The engine only honors breakpoints
14//! while they are switched on, which is a separate decision made at two levels.
15//! [`Engine::breakpoints_enabled`](crate::Engine::breakpoints_enabled) is the
16//! live switch for the session, and the station option of the same name is the
17//! setting written to disk. With either off, a set breakpoint stays set and is
18//! ignored.
19
20use rs_teststand_sys::Value;
21
22/// Which run a breakpoint applies to.
23///
24/// The engine takes this as an optional argument on every breakpoint member.
25/// Leaving it out edits the step itself; supplying an execution scopes the
26/// change to that run.
27#[derive(Debug, Clone, Copy)]
28pub enum BreakpointScope<'execution> {
29 /// Write the breakpoint into the step, where it outlives every run and is
30 /// saved with the sequence file.
31 Step,
32 /// Apply it to one execution only, leaving the step on disk alone.
33 Execution(&'execution crate::Execution),
34}
35
36impl BreakpointScope<'_> {
37 /// The optional execution argument the engine expects.
38 ///
39 /// [`Step`](Self::Step) becomes an absent argument rather than a null one,
40 /// which is how the engine is told to edit the step itself.
41 pub(crate) fn argument(self) -> Value {
42 match self {
43 Self::Step => Value::Empty,
44 Self::Execution(execution) => execution.as_argument(),
45 }
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use rs_teststand_sys::Value;
52
53 use super::BreakpointScope;
54
55 #[test]
56 fn step_scope_sends_an_absent_argument() {
57 // Absent, not null. The engine reads a missing execution as "edit the
58 // step itself"; a null object would be a different request.
59 assert!(matches!(BreakpointScope::Step.argument(), Value::Empty));
60 }
61}