dpm_model/run/
lifecycle.rs1use crate::{ActorId, RunEventId, RunId};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::str::FromStr;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum RunState {
15 Working,
17 Waiting,
19 Failed,
21 Interrupted,
23 Completed,
25}
26
27impl RunState {
28 #[must_use]
30 pub fn is_terminal(self) -> bool {
31 matches!(self, Self::Failed | Self::Interrupted | Self::Completed)
32 }
33
34 #[must_use]
36 pub fn word(self) -> &'static str {
37 match self {
38 Self::Working => "working",
39 Self::Waiting => "waiting",
40 Self::Failed => "failed",
41 Self::Interrupted => "interrupted",
42 Self::Completed => "completed",
43 }
44 }
45}
46
47impl std::fmt::Display for RunState {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.write_str(self.word())
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
55#[error("unknown run state {0:?}; expected working, waiting, failed, interrupted or completed")]
56pub struct RunStateParseError(String);
57
58impl FromStr for RunState {
59 type Err = RunStateParseError;
60
61 fn from_str(word: &str) -> Result<Self, Self::Err> {
62 [
63 Self::Working,
64 Self::Waiting,
65 Self::Failed,
66 Self::Interrupted,
67 Self::Completed,
68 ]
69 .into_iter()
70 .find(|state| state.word() == word)
71 .ok_or_else(|| RunStateParseError(word.into()))
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(deny_unknown_fields)]
78pub struct RunTransition {
79 pub id: RunEventId,
81 pub run: RunId,
83 pub to: RunState,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub detail: Option<String>,
88 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub observed_at: Option<DateTime<Utc>>,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
95#[serde(deny_unknown_fields)]
96pub struct LifecycleEvent {
97 pub id: RunEventId,
99 pub run: RunId,
101 pub state: RunState,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub detail: Option<String>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub observed_at: Option<DateTime<Utc>>,
109 pub recorded_by: ActorId,
111 pub recorded_at: DateTime<Utc>,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct LifecycleEntry {
118 pub sequence: u64,
120 #[serde(flatten)]
122 pub event: LifecycleEvent,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub struct LifecyclePage {
128 pub entries: Vec<LifecycleEntry>,
130 pub next_after_sequence: u64,
132 pub head_sequence: u64,
134}