summaryrefslogtreecommitdiff
path: root/makima/src/daemon/task/state.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-11 05:52:14 +0000
committersoryu <soryu@soryu.co>2026-01-15 00:21:16 +0000
commit87044a747b47bd83249d61a45842c7f7b2eae56d (patch)
treeef2000ce79ffcc2723ef841acef5aa1deb1d5378 /makima/src/daemon/task/state.rs
parent077820c4167c168072d217a1b01df840463a12a8 (diff)
downloadsoryu-87044a747b47bd83249d61a45842c7f7b2eae56d.tar.gz
soryu-87044a747b47bd83249d61a45842c7f7b2eae56d.zip
Contract system
Diffstat (limited to 'makima/src/daemon/task/state.rs')
-rw-r--r--makima/src/daemon/task/state.rs161
1 files changed, 161 insertions, 0 deletions
diff --git a/makima/src/daemon/task/state.rs b/makima/src/daemon/task/state.rs
new file mode 100644
index 0000000..ca5fc01
--- /dev/null
+++ b/makima/src/daemon/task/state.rs
@@ -0,0 +1,161 @@
+//! Task state machine.
+
+use std::fmt;
+
+/// Task execution state.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum TaskState {
+ /// Task received, preparing overlay.
+ Initializing,
+ /// Overlay ready, starting container.
+ Starting,
+ /// Container running.
+ Running,
+ /// Container paused.
+ Paused,
+ /// Waiting for sibling or resource.
+ Blocked,
+ /// Task completed successfully.
+ Completed,
+ /// Task failed with error.
+ Failed,
+ /// Task interrupted by user.
+ Interrupted,
+}
+
+impl TaskState {
+ /// Check if a state transition is valid.
+ pub fn can_transition_to(&self, target: TaskState) -> bool {
+ use TaskState::*;
+
+ matches!(
+ (self, target),
+ // From Initializing
+ (Initializing, Starting)
+ | (Initializing, Failed)
+ | (Initializing, Interrupted)
+ // From Starting
+ | (Starting, Running)
+ | (Starting, Failed)
+ | (Starting, Interrupted)
+ // From Running
+ | (Running, Paused)
+ | (Running, Blocked)
+ | (Running, Completed)
+ | (Running, Failed)
+ | (Running, Interrupted)
+ // From Paused
+ | (Paused, Running)
+ | (Paused, Interrupted)
+ | (Paused, Failed)
+ // From Blocked
+ | (Blocked, Running)
+ | (Blocked, Failed)
+ | (Blocked, Interrupted)
+ )
+ }
+
+ /// Check if this state is terminal (no more transitions possible).
+ pub fn is_terminal(&self) -> bool {
+ matches!(
+ self,
+ TaskState::Completed | TaskState::Failed | TaskState::Interrupted
+ )
+ }
+
+ /// Check if the task is currently active (running or paused).
+ pub fn is_active(&self) -> bool {
+ matches!(
+ self,
+ TaskState::Initializing
+ | TaskState::Starting
+ | TaskState::Running
+ | TaskState::Paused
+ | TaskState::Blocked
+ )
+ }
+
+ /// Check if the task is running.
+ pub fn is_running(&self) -> bool {
+ matches!(self, TaskState::Running)
+ }
+
+ /// Convert to string for protocol messages.
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ TaskState::Initializing => "initializing",
+ TaskState::Starting => "starting",
+ TaskState::Running => "running",
+ TaskState::Paused => "paused",
+ TaskState::Blocked => "blocked",
+ TaskState::Completed => "done",
+ TaskState::Failed => "failed",
+ TaskState::Interrupted => "interrupted",
+ }
+ }
+
+ /// Parse from string.
+ pub fn from_str(s: &str) -> Option<Self> {
+ match s.to_lowercase().as_str() {
+ "initializing" => Some(TaskState::Initializing),
+ "starting" => Some(TaskState::Starting),
+ "running" => Some(TaskState::Running),
+ "paused" => Some(TaskState::Paused),
+ "blocked" => Some(TaskState::Blocked),
+ "done" | "completed" => Some(TaskState::Completed),
+ "failed" => Some(TaskState::Failed),
+ "interrupted" => Some(TaskState::Interrupted),
+ _ => None,
+ }
+ }
+}
+
+impl fmt::Display for TaskState {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.as_str())
+ }
+}
+
+impl Default for TaskState {
+ fn default() -> Self {
+ TaskState::Initializing
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::daemon::*;
+
+ #[test]
+ fn test_valid_transitions() {
+ use TaskState::*;
+
+ // Valid transitions
+ assert!(Initializing.can_transition_to(Starting));
+ assert!(Starting.can_transition_to(Running));
+ assert!(Running.can_transition_to(Completed));
+ assert!(Running.can_transition_to(Paused));
+ assert!(Paused.can_transition_to(Running));
+
+ // Invalid transitions
+ assert!(!Completed.can_transition_to(Running));
+ assert!(!Failed.can_transition_to(Running));
+ assert!(!Running.can_transition_to(Initializing));
+ }
+
+ #[test]
+ fn test_terminal_states() {
+ assert!(TaskState::Completed.is_terminal());
+ assert!(TaskState::Failed.is_terminal());
+ assert!(TaskState::Interrupted.is_terminal());
+ assert!(!TaskState::Running.is_terminal());
+ assert!(!TaskState::Paused.is_terminal());
+ }
+
+ #[test]
+ fn test_parse() {
+ assert_eq!(TaskState::from_str("running"), Some(TaskState::Running));
+ assert_eq!(TaskState::from_str("done"), Some(TaskState::Completed));
+ assert_eq!(TaskState::from_str("invalid"), None);
+ }
+}