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