#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use strum::{AsRefStr, EnumString};
use time::OffsetDateTime;
use url::Url;
use crate::Hateoas;
#[cfg(feature = "serde")]
use super::utils;
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "SCREAMING_SNAKE_CASE")
)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString)]
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
pub enum TaskStatusType {
Received,
Pending,
Scheduled,
Rejected,
Denied,
MovedVis,
Bumped,
Moved,
Cancelled,
SystemError,
QueuedPass,
Configured,
Downlinking,
Recording,
CompletedPass,
DataCloud,
Processing,
ReadyCustom,
ProcessedCustom,
ErrorCustom,
Completed,
CompletedError,
PushedToCustomer,
ErrorPushToCustomer,
Invoiced,
Paid,
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
pub struct TaskStatus {
#[cfg_attr(feature = "serde", serde(with = "crate::utils::timestamp"))]
pub created: OffsetDateTime,
pub status: TaskStatusType,
pub reason: String,
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "SCREAMING_SNAKE_CASE")
)]
#[derive(
Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString,
)]
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
pub enum Polarization {
#[default]
Right,
Left,
Vertical,
Horizontal,
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "SCREAMING_SNAKE_CASE")
)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString)]
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
pub enum TaskType {
Before,
After,
Test,
Around,
Exact,
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
pub struct Task {
#[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
pub created: OffsetDateTime,
#[cfg_attr(
feature = "serde",
serde(default, with = "time::serde::iso8601::option")
)]
pub modified: Option<OffsetDateTime>,
pub found_visibility: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub internal_meta_data: Option<HashMap<String, String>>,
#[cfg_attr(feature = "serde", serde(default))]
pub score: Option<u8>,
#[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
pub start: OffsetDateTime,
#[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
pub end: OffsetDateTime,
#[cfg_attr(
feature = "serde",
serde(default, with = "time::serde::iso8601::option")
)]
pub visibility_start: Option<OffsetDateTime>,
#[cfg_attr(
feature = "serde",
serde(default, with = "time::serde::iso8601::option")
)]
pub visibility_end: Option<OffsetDateTime>,
pub billable: bool,
pub duration_in_seconds: u32,
pub task_within_config_window: bool,
pub duration: String,
pub file_results: Vec<String>,
#[cfg_attr(feature = "serde", serde(default))]
pub meta_data: Option<HashMap<String, String>>,
#[cfg_attr(
feature = "serde",
serde(rename = "_links", with = "utils::links::serde", default)
)]
pub links: HashMap<String, Url>,
}
impl Hateoas for Task {
fn get_links(&self) -> &HashMap<String, url::Url> {
&self.links
}
fn get_links_mut(&mut self) -> &mut HashMap<String, url::Url> {
&mut self.links
}
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
pub struct TaskStatusEvent {
pub task_request_id: i32,
pub task_request_uri: String,
pub status_changes: Vec<TaskStatus>,
}
impl TaskStatusEvent {
pub fn latest_status(&self) -> Option<&TaskStatus> {
self.status_changes
.iter()
.max_by_key(|status| status.created)
}
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
pub struct TaskRequest {
#[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
pub created: OffsetDateTime,
#[cfg_attr(
feature = "serde",
serde(default, with = "time::serde::iso8601::option")
)]
pub modified: Option<OffsetDateTime>,
#[cfg_attr(feature = "serde", serde(default))]
pub internal_meta_data: Option<HashMap<String, String>>,
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub task_type: TaskType,
#[cfg_attr(feature = "serde", serde(default))]
pub hours_of_flex: u32,
pub duration: u32,
pub minimum_duration: u32,
#[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
pub target_date: OffsetDateTime,
#[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
pub earliest_start: OffsetDateTime,
#[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
pub latest_start: OffsetDateTime,
pub transmitting: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub test_file: Option<String>,
pub status_changes: Vec<TaskStatus>,
pub task_active: bool,
pub task_request_scheduled: bool,
pub task_request_cancelled: bool,
pub flex: bool,
pub latest_status_change: TaskStatus,
#[cfg_attr(feature = "serde", serde(default))]
pub meta_data: Option<HashMap<String, String>>,
#[cfg_attr(
feature = "serde",
serde(rename = "_links", with = "utils::links::serde", default)
)]
pub links: HashMap<String, Url>,
}
impl Hateoas for TaskRequest {
fn get_links(&self) -> &HashMap<String, url::Url> {
&self.links
}
fn get_links_mut(&mut self) -> &mut HashMap<String, url::Url> {
&mut self.links
}
}
#[cfg(all(test, feature = "serde"))]
mod tests {
use super::*;
#[test]
fn task_status_event() {
let json = r#"
{
"taskRequestId": 189348,
"taskRequestUri": "https://test-api.atlasground.com/api/requests/189348",
"statusChanges": [
{
"created": "2025-08-01T19:55:07.061Z",
"status": "RECEIVED",
"reason": "Saved to Database and awaiting scheduling"
},
{
"created": "2025-08-01T19:55:07.674Z",
"status": "SCHEDULED",
"reason": "Test Task Scheduled"
},
{
"created": "2025-08-01T19:55:08.986Z",
"status": "QUEUED_PASS",
"reason": "Pass has been queued to execute"
}
]
}"#;
let event: TaskStatusEvent = serde_json::from_str(json).unwrap();
assert_eq!(
event.latest_status().unwrap().status,
TaskStatusType::QueuedPass
);
}
#[test]
fn task_status_event_float_timestamp() {
use time::macros::datetime;
let json = r#"
{
"taskRequestId": 190029,
"taskRequestUri": "https://test-api.atlasground.com/api/requests/190029",
"statusChanges": [
{
"created": 1754409946.362000000,
"status": "RECEIVED",
"reason": "Saved to Database and awaiting scheduling"
}
]
}"#;
let event: TaskStatusEvent = serde_json::from_str(json).unwrap();
assert_eq!(
datetime!(2025 - 08 - 05 16:05:46.361_999_989).assume_utc(),
event.status_changes[0].created
);
}
}