Skip to main content

actionqueue_core/run/
state.rs

1//! Run state definitions for the task execution lifecycle.
2
3/// Canonical states in the run lifecycle.
4///
5/// States progress forward through: Scheduled -> Ready -> Leased -> Running -> (RetryWait -> Ready)* or -> Terminal
6/// A running attempt may also be preempted to Suspended (e.g. by budget exhaustion), then
7/// resumed back to Ready when capacity is restored.
8/// Cancellation is also allowed from Scheduled, Ready, Leased, Running, RetryWait, and Suspended -> Canceled.
9/// Terminal states (Completed, Failed, Canceled) are immutable and cannot transition to any other state.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub enum RunState {
13    /// The run is scheduled and waiting to become ready.
14    /// Transitions: -> Ready (when scheduled_at has passed), -> Canceled
15    Scheduled,
16
17    /// The run is ready to be leased/ picked up by an executor.
18    /// Transitions: -> Leased (when leased), -> Canceled
19    Ready,
20
21    /// The run has been leased to an executor for processing.
22    /// Transitions: -> Running (when execution starts), -> Ready (lease expired), -> Canceled
23    Leased,
24
25    /// The run is currently being executed.
26    /// Transitions: -> RetryWait (on failure, if retries remain), -> Suspended (preempted),
27    /// -> Completed (on success), -> Failed (on failure, no retries), -> Canceled
28    Running,
29
30    /// The run failed and is waiting before retry.
31    /// Transitions: -> Ready (when backoff completes), -> Failed (if no more retries remain), -> Canceled
32    RetryWait,
33
34    /// The run has been preempted (e.g. budget exhaustion) and is waiting for resumption.
35    /// Non-terminal — will resume to Ready when capacity is restored.
36    /// Suspended attempts do not count toward the max_attempts retry cap.
37    /// Transitions: -> Ready (when budget replenished / explicit resume), -> Canceled
38    Suspended,
39
40    /// The run completed successfully.
41    /// Terminal state - no further transitions allowed.
42    Completed,
43
44    /// The run failed after all retries were exhausted.
45    /// Terminal state - no further transitions allowed.
46    Failed,
47
48    /// The run was canceled.
49    /// Terminal state - no further transitions allowed.
50    Canceled,
51}
52
53impl RunState {
54    /// Returns true if this is a terminal state.
55    pub fn is_terminal(&self) -> bool {
56        matches!(self, RunState::Completed | RunState::Failed | RunState::Canceled)
57    }
58}
59
60impl std::fmt::Display for RunState {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        let name = match self {
63            RunState::Scheduled => "scheduled",
64            RunState::Ready => "ready",
65            RunState::Leased => "leased",
66            RunState::Running => "running",
67            RunState::RetryWait => "retry_wait",
68            RunState::Suspended => "suspended",
69            RunState::Completed => "completed",
70            RunState::Failed => "failed",
71            RunState::Canceled => "canceled",
72        };
73        write!(f, "{name}")
74    }
75}