1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use strum::{AsRefStr, EnumString};
5use time::OffsetDateTime;
6use url::Url;
7
8use crate::Hateoas;
9
10#[cfg(feature = "serde")]
11use super::utils;
12
13#[cfg_attr(
14 feature = "serde",
15 derive(Serialize, Deserialize),
16 serde(rename_all = "SCREAMING_SNAKE_CASE")
17)]
18#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString)]
19#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
20#[non_exhaustive]
21pub enum TaskStatusType {
22 Received,
23 Pending,
24 Scheduled,
25 Rejected,
26 Denied,
27 MovedVis,
28 Bumped,
29 Moved,
30 Cancelled,
31 SystemError,
32 QueuedPass,
33 Configured,
34 Downlinking,
35 Recording,
36 CompletedPass,
37 DataCloud,
38 Processing,
39 ReadyCustom,
40 ProcessedCustom,
41 ErrorCustom,
42 Completed,
43 CompletedError,
44 PushedToCustomer,
45 ErrorPushToCustomer,
46 Invoiced,
47 Paid,
48}
49
50#[cfg_attr(
51 feature = "serde",
52 derive(Serialize, Deserialize),
53 serde(rename_all = "camelCase")
54)]
55#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
56pub struct TaskStatus {
57 #[cfg_attr(feature = "serde", serde(with = "crate::utils::timestamp"))]
58 pub created: OffsetDateTime,
59 pub status: TaskStatusType,
60 pub reason: String,
61}
62
63#[cfg_attr(
64 feature = "serde",
65 derive(Serialize, Deserialize),
66 serde(rename_all = "SCREAMING_SNAKE_CASE")
67)]
68#[derive(
69 Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString,
70)]
71#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
72#[non_exhaustive]
73pub enum Polarization {
74 #[default]
75 Right,
76 Left,
77 Vertical,
78 Horizontal,
79}
80
81#[cfg_attr(
82 feature = "serde",
83 derive(Serialize, Deserialize),
84 serde(rename_all = "SCREAMING_SNAKE_CASE")
85)]
86#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString)]
87#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
88#[non_exhaustive]
89pub enum TaskType {
90 Before,
91 After,
92 Test,
93 Around,
94 Exact,
95}
96
97#[cfg_attr(
98 feature = "serde",
99 derive(Serialize, Deserialize),
100 serde(rename_all = "camelCase")
101)]
102#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct Task {
104 #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
105 pub created: OffsetDateTime,
106 #[cfg_attr(
107 feature = "serde",
108 serde(default, with = "time::serde::iso8601::option")
109 )]
110 pub modified: Option<OffsetDateTime>,
111 pub found_visibility: bool,
112 #[cfg_attr(feature = "serde", serde(default))]
114 pub internal_meta_data: Option<HashMap<String, String>>,
115 #[cfg_attr(feature = "serde", serde(default))]
117 pub score: Option<u8>,
118 #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
119 pub start: OffsetDateTime,
120 #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
121 pub end: OffsetDateTime,
122 #[cfg_attr(
123 feature = "serde",
124 serde(default, with = "time::serde::iso8601::option")
125 )]
126 pub visibility_start: Option<OffsetDateTime>,
127 #[cfg_attr(
128 feature = "serde",
129 serde(default, with = "time::serde::iso8601::option")
130 )]
131 pub visibility_end: Option<OffsetDateTime>,
132 pub billable: bool,
133 pub duration_in_seconds: u32,
134 pub task_within_config_window: bool,
135 pub duration: String,
136 pub file_results: Vec<String>,
137 #[cfg_attr(feature = "serde", serde(default))]
138 pub meta_data: Option<HashMap<String, String>>,
139 #[cfg_attr(
140 feature = "serde",
141 serde(rename = "_links", with = "utils::links::serde", default)
142 )]
143 pub links: HashMap<String, Url>,
144}
145
146impl Hateoas for Task {
147 fn get_links(&self) -> &HashMap<String, url::Url> {
148 &self.links
149 }
150
151 fn get_links_mut(&mut self) -> &mut HashMap<String, url::Url> {
152 &mut self.links
153 }
154}
155
156#[cfg_attr(
157 feature = "serde",
158 derive(Serialize, Deserialize),
159 serde(rename_all = "camelCase")
160)]
161#[derive(Debug, Clone, PartialEq, Eq)]
162pub struct TaskStatusEvent {
163 pub task_request_id: i32,
164 pub task_request_uri: String,
165 pub status_changes: Vec<TaskStatus>,
166}
167
168impl TaskStatusEvent {
169 pub fn latest_status(&self) -> Option<&TaskStatus> {
170 self.status_changes
171 .iter()
172 .max_by_key(|status| status.created)
173 }
174}
175
176#[cfg_attr(
177 feature = "serde",
178 derive(Serialize, Deserialize),
179 serde(rename_all = "camelCase")
180)]
181#[derive(Debug, Clone, PartialEq, Eq)]
182pub struct TaskRequest {
183 #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
184 pub created: OffsetDateTime,
185 #[cfg_attr(
186 feature = "serde",
187 serde(default, with = "time::serde::iso8601::option")
188 )]
189 pub modified: Option<OffsetDateTime>,
190 #[cfg_attr(feature = "serde", serde(default))]
192 pub internal_meta_data: Option<HashMap<String, String>>,
193 #[cfg_attr(feature = "serde", serde(rename = "type"))]
194 pub task_type: TaskType,
195 #[cfg_attr(feature = "serde", serde(default))]
196 pub hours_of_flex: u32,
197 pub duration: u32,
198 pub minimum_duration: u32,
199 #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
200 pub target_date: OffsetDateTime,
201 #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
202 pub earliest_start: OffsetDateTime,
203 #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
204 pub latest_start: OffsetDateTime,
205 pub transmitting: bool,
206 #[cfg_attr(feature = "serde", serde(default))]
207 pub test_file: Option<String>,
208 pub status_changes: Vec<TaskStatus>,
209 pub task_active: bool,
210 pub task_request_scheduled: bool,
211 pub task_request_cancelled: bool,
212 pub flex: bool,
213 pub latest_status_change: TaskStatus,
214 #[cfg_attr(feature = "serde", serde(default))]
215 pub meta_data: Option<HashMap<String, String>>,
216 #[cfg_attr(
217 feature = "serde",
218 serde(rename = "_links", with = "utils::links::serde", default)
219 )]
220 pub links: HashMap<String, Url>,
221}
222
223impl Hateoas for TaskRequest {
224 fn get_links(&self) -> &HashMap<String, url::Url> {
225 &self.links
226 }
227
228 fn get_links_mut(&mut self) -> &mut HashMap<String, url::Url> {
229 &mut self.links
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 #[cfg(feature = "serde")]
239 fn task_status_event() {
240 let json = r#"
241{
242 "taskRequestId": 189348,
243 "taskRequestUri": "https://test-api.atlasground.com/api/requests/189348",
244 "statusChanges": [
245 {
246 "created": "2025-08-01T19:55:07.061Z",
247 "status": "RECEIVED",
248 "reason": "Saved to Database and awaiting scheduling"
249 },
250 {
251 "created": "2025-08-01T19:55:07.674Z",
252 "status": "SCHEDULED",
253 "reason": "Test Task Scheduled"
254 },
255 {
256 "created": "2025-08-01T19:55:08.986Z",
257 "status": "QUEUED_PASS",
258 "reason": "Pass has been queued to execute"
259 }
260 ]
261}"#;
262 let event: TaskStatusEvent = serde_json::from_str(json).unwrap();
263 assert_eq!(
264 event.latest_status().unwrap().status,
265 TaskStatusType::QueuedPass
266 );
267 }
268
269 #[test]
270 #[cfg(feature = "serde")]
271 fn task_status_event_float_timestamp() {
272 use time::macros::datetime;
273
274 let json = r#"
275{
276 "taskRequestId": 190029,
277 "taskRequestUri": "https://test-api.atlasground.com/api/requests/190029",
278 "statusChanges": [
279 {
280 "created": 1754409946.362000000,
281 "status": "RECEIVED",
282 "reason": "Saved to Database and awaiting scheduling"
283 }
284 ]
285}"#;
286 let event: TaskStatusEvent = serde_json::from_str(json).unwrap();
287 assert_eq!(
288 datetime!(2025 - 08 - 05 16:05:46.361_999_989).assume_utc(),
289 event.status_changes[0].created
290 );
291 }
292}