Skip to main content

loong_contracts/
task_state.rs

1use serde::{Deserialize, Serialize};
2
3use crate::contracts::{HarnessOutcome, TaskIntent};
4use crate::fault::Fault;
5
6/// State machine for task lifecycle.
7///
8/// Valid transitions:
9/// - Runnable -> InSend
10/// - InSend -> InReply
11/// - InReply -> Completed | Faulted
12/// - Any non-terminal -> Faulted
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub enum TaskState {
15    Runnable(TaskIntent),
16    InSend { task_id: String },
17    InReply { task_id: String },
18    Completed(HarnessOutcome),
19    Faulted(Fault),
20}
21
22impl TaskState {
23    pub fn task_id(&self) -> Option<&str> {
24        match self {
25            Self::Runnable(intent) => Some(&intent.task_id),
26            Self::InSend { task_id } | Self::InReply { task_id } => Some(task_id),
27            Self::Completed(_) | Self::Faulted(_) => None,
28        }
29    }
30
31    pub fn is_terminal(&self) -> bool {
32        matches!(self, Self::Completed(_) | Self::Faulted(_))
33    }
34
35    pub fn transition_to_in_send(self) -> Result<Self, String> {
36        #[allow(clippy::wildcard_enum_match_arm)]
37        match self {
38            Self::Runnable(intent) => Ok(Self::InSend {
39                task_id: intent.task_id,
40            }),
41            other => Err(format!(
42                "invalid transition: cannot move to InSend from {other:?}"
43            )),
44        }
45    }
46
47    pub fn transition_to_in_reply(self) -> Result<Self, String> {
48        #[allow(clippy::wildcard_enum_match_arm)]
49        match self {
50            Self::InSend { task_id } => Ok(Self::InReply { task_id }),
51            other => Err(format!(
52                "invalid transition: cannot move to InReply from {other:?}"
53            )),
54        }
55    }
56
57    pub fn transition_to_completed(self, outcome: HarnessOutcome) -> Result<Self, String> {
58        #[allow(clippy::wildcard_enum_match_arm)]
59        match self {
60            Self::InReply { .. } => Ok(Self::Completed(outcome)),
61            other => Err(format!(
62                "invalid transition: cannot move to Completed from {other:?}"
63            )),
64        }
65    }
66
67    pub fn transition_to_faulted(self, fault: Fault) -> Self {
68        if self.is_terminal() {
69            self // Already terminal -- ignore
70        } else {
71            Self::Faulted(fault)
72        }
73    }
74}