Skip to main content

muster/domain/process/
state.rs

1use strum::Display;
2
3/// Lifecycle state of a managed process. The TUI adapter maps this to a sidebar
4/// status glyph and color; the domain itself stays free of rendering concerns.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Display)]
6#[strum(serialize_all = "lowercase")]
7pub enum ProcessState {
8    /// Configured but not yet started.
9    #[default]
10    Pending,
11    /// Child process is alive.
12    Running,
13    /// Child process is alive but suspended (SIGSTOP).
14    Paused,
15    /// A stop or restart signal was delivered and exit is pending.
16    Stopping,
17    /// Exited successfully (status 0).
18    Exited,
19    /// Exited with a failure status or was killed by a signal.
20    Crashed,
21    /// Scheduled to restart after a backoff.
22    Restarting,
23}
24
25impl ProcessState {
26    /// Whether the process currently has a live child attached.
27    pub fn is_active(self) -> bool {
28        matches!(
29            self,
30            Self::Running | Self::Paused | Self::Stopping | Self::Restarting
31        )
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn defaults_to_pending() {
41        assert_eq!(ProcessState::default(), ProcessState::Pending);
42    }
43
44    #[test]
45    fn active_while_a_child_is_live_or_transitioning() {
46        assert!(ProcessState::Running.is_active());
47        assert!(ProcessState::Paused.is_active());
48        assert!(ProcessState::Stopping.is_active());
49        assert!(ProcessState::Restarting.is_active());
50        assert!(!ProcessState::Pending.is_active());
51        assert!(!ProcessState::Exited.is_active());
52        assert!(!ProcessState::Crashed.is_active());
53    }
54}