Skip to main content

yarli_cli/yarli-core/src/fsm/
command.rs

1//! Command execution state machine (Section 7.5).
2//!
3//! Rules:
4//! - Every command has start timestamp, end timestamp, exit reason, and exit code.
5//! - Stderr/stdout chunks must be associated with command ID and sequence number.
6
7use serde::{Deserialize, Serialize};
8
9/// Command execution lifecycle states.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
12pub enum CommandState {
13    CmdQueued,
14    CmdStarted,
15    CmdStreaming,
16    CmdExited,
17    CmdTimedOut,
18    CmdKilled,
19}
20
21impl CommandState {
22    /// Terminal states cannot transition further.
23    pub fn is_terminal(self) -> bool {
24        matches!(
25            self,
26            CommandState::CmdExited | CommandState::CmdTimedOut | CommandState::CmdKilled
27        )
28    }
29
30    /// Returns the set of states reachable from this state.
31    pub fn valid_transitions(self) -> &'static [CommandState] {
32        use CommandState::*;
33        match self {
34            CmdQueued => &[CmdStarted, CmdKilled],
35            CmdStarted => &[CmdStreaming, CmdExited, CmdTimedOut, CmdKilled],
36            CmdStreaming => &[CmdExited, CmdTimedOut, CmdKilled],
37            CmdExited => &[],
38            CmdTimedOut => &[],
39            CmdKilled => &[],
40        }
41    }
42
43    /// Check if transitioning to `target` is valid.
44    pub fn can_transition_to(self, target: CommandState) -> bool {
45        self.valid_transitions().contains(&target)
46    }
47}