1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum TaskState {
9 Submitted,
11 Working,
13 InputRequired,
15 Completed,
17 Failed,
19 Cancelled,
21}
22
23impl TaskState {
24 pub fn is_terminal(self) -> bool {
26 matches!(
27 self,
28 TaskState::Completed | TaskState::Failed | TaskState::Cancelled
29 )
30 }
31
32 pub fn can_transition(from: TaskState, to: TaskState) -> bool {
34 use TaskState::*;
35 if from == to {
36 return false;
37 }
38 if from.is_terminal() {
39 return false;
40 }
41 if matches!(to, Failed | Cancelled) {
42 return true;
43 }
44 matches!(
45 (from, to),
46 (Submitted, Working)
47 | (Working, InputRequired)
48 | (Working, Completed)
49 | (InputRequired, Working)
50 )
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56pub struct Task {
57 pub id: Uuid,
59 pub team_id: String,
61 pub title: String,
63 pub state: TaskState,
65 pub owner: String,
67 pub parent_task_id: Option<Uuid>,
69 pub requirement_id: Option<String>,
71 pub epic_id: Option<String>,
73 pub created_at: DateTime<Utc>,
75 pub updated_at: DateTime<Utc>,
77}
78
79impl Task {
80 pub fn new(
82 team_id: impl Into<String>,
83 title: impl Into<String>,
84 owner: impl Into<String>,
85 ) -> Self {
86 let now = Utc::now();
87 Task {
88 id: Uuid::new_v4(),
89 team_id: team_id.into(),
90 title: title.into(),
91 state: TaskState::Submitted,
92 owner: owner.into(),
93 parent_task_id: None,
94 requirement_id: None,
95 epic_id: None,
96 created_at: now,
97 updated_at: now,
98 }
99 }
100}