1use crate::{task::TaskId, AsyncJobBoxed};
3use std::{
4 fmt::{Debug, Display},
5 time::Duration,
6};
7use uuid::Uuid;
8
9#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
38#[non_exhaustive]
39pub struct JobId {
40 pub task_id: TaskId,
42 pub id: Uuid,
44}
45
46impl JobId {
47 pub(crate) fn new(task_id: impl Into<TaskId>) -> Self {
48 Self {
49 id: Uuid::new_v4(),
50 task_id: task_id.into(),
51 }
52 }
53}
54
55impl From<TaskId> for JobId {
56 fn from(value: TaskId) -> Self {
57 Self {
58 id: Uuid::new_v4(),
59 task_id: value,
60 }
61 }
62}
63
64impl From<&TaskId> for JobId {
65 fn from(value: &TaskId) -> Self {
66 Self {
67 id: Uuid::new_v4(),
68 task_id: value.to_owned(),
69 }
70 }
71}
72
73impl From<JobId> for String {
74 fn from(value: JobId) -> Self {
75 format!("{}/{}", value.task_id, value.id)
76 }
77}
78
79impl Display for JobId {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 write!(f, "{}/{}", self.task_id, self.id)
82 }
83}
84
85pub(crate) struct Job {
86 id: JobId,
87 job: AsyncJobBoxed,
88 timeout: Option<Duration>,
89}
90
91impl Job {
92 pub(crate) fn new(id: JobId, job: AsyncJobBoxed, timeout: Option<Duration>) -> Self {
93 Self { id, job, timeout }
94 }
95
96 pub(crate) fn id(&self) -> JobId {
97 self.id.clone()
98 }
99
100 pub(crate) fn job(&self) -> AsyncJobBoxed {
101 self.job.clone()
102 }
103
104 pub(crate) fn timeout(&self) -> Option<Duration> {
105 self.timeout
106 }
107}
108
109impl Debug for Job {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 f.debug_struct("Job")
112 .field("id", &self.id)
113 .field("timeout", &self.timeout)
114 .finish()
115 }
116}
117
118#[derive(Debug, Default, PartialEq, Clone)]
119pub(crate) enum JobState {
120 #[default]
121 Pending,
122 Starting,
123 Running,
124 Completed,
125 Canceled,
126 Timeout,
127 Error,
128}
129
130impl JobState {
131 pub fn finished(&self) -> bool {
132 *self == JobState::Completed
133 || *self == JobState::Canceled
134 || *self == JobState::Timeout
135 || *self == JobState::Error
136 }
137}
138
139#[cfg(test)]
140mod test {
141 use super::*;
142 use crate::task::{Task, TaskSchedule};
143
144 #[test]
145 fn job_state_finished() {
146 assert!(!JobState::Pending.finished());
147 assert!(!JobState::Starting.finished());
148 assert!(!JobState::Running.finished());
149 assert!(JobState::Completed.finished());
150 assert!(JobState::Canceled.finished());
151 assert!(JobState::Timeout.finished());
152 assert!(JobState::Error.finished());
153 }
154
155 #[test]
156 fn type_convertors() {
157 let task_id = TaskId::from("TASK_ID");
158 let job_id = JobId::from(task_id.clone());
159
160 assert_eq!(JobId::from(task_id.clone()).task_id, task_id);
161 assert_eq!(JobId::from(&task_id).task_id, task_id);
162
163 assert_eq!(
164 format!("{job_id}"),
165 format!("{}/{}", job_id.task_id, job_id.id)
166 );
167 assert_eq!(
168 String::from(job_id.clone()),
169 format!("{}/{}", job_id.task_id, job_id.id)
170 );
171 }
172
173 #[test]
174 fn debug_formatter() {
175 let task1 = Task::new(TaskSchedule::Once, |_id| Box::pin(async move {})).with_id("TEST");
176 let task2 = Task::new(TaskSchedule::Once, |_id| Box::pin(async move {}))
177 .with_id("TEST_WITH_TIMEOUT")
178 .with_timeout(Duration::from_secs(1));
179
180 let job1 = Job::new(JobId::new(task1.id()), task1.job, None);
181 let job2 = Job::new(
182 JobId::new(task2.id()),
183 task2.job,
184 Some(Duration::from_secs(1)),
185 );
186
187 assert_eq!(format!("{job1:?}"), format!("Job {{ id: JobId {{ task_id: TaskId {{ id: \"TEST\" }}, id: {} }}, timeout: None }}",job1.id().id));
188 assert_eq!(format!("{job2:?}"), format!("Job {{ id: JobId {{ task_id: TaskId {{ id: \"TEST_WITH_TIMEOUT\" }}, id: {} }}, timeout: Some(1s) }}", job2.id().id));
189 }
190}