use serde::{Deserialize, Serialize};
use stack_ids::{AttemptId, TraceCtx, TrialId};
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchedulingConfig {
pub max_consecutive_same_key: usize,
#[serde(with = "duration_millis")]
pub resource_switch_cooldown: Duration,
pub enable_reordering: bool,
}
impl Default for SchedulingConfig {
fn default() -> Self {
Self {
max_consecutive_same_key: 3,
resource_switch_cooldown: Duration::ZERO,
enable_reordering: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EtaConfidence {
Low,
Medium,
High,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EtaEstimate {
pub remaining_ms: u64,
pub items_remaining: usize,
pub avg_item_ms: u64,
pub confidence: EtaConfidence,
pub sample_count: u64,
}
mod duration_millis {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::time::Duration;
pub fn serialize<S: Serializer>(d: &Duration, s: S) -> Result<S::Ok, S::Error> {
d.as_millis().serialize(s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Duration, D::Error> {
let ms = u64::deserialize(d)?;
Ok(Duration::from_millis(ms))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum BatchItemStatus {
Pending,
Running,
Completed,
Failed,
Skipped,
Cancelled,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum BatchJobStatus {
Queued,
Running,
Completed,
CompletedWithErrors,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum OverwritePolicy {
Skip,
Overwrite,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SizeBucket {
Small,
Medium,
Large,
Unknown,
}
impl SizeBucket {
pub fn from_pixel_count(pixels: u64) -> Self {
if pixels < 500_000 {
Self::Small
} else if pixels < 2_000_000 {
Self::Medium
} else {
Self::Large
}
}
pub fn from_dimensions(width: Option<u32>, height: Option<u32>) -> Self {
match (width, height) {
(Some(w), Some(h)) => Self::from_pixel_count(w as u64 * h as u64),
_ => Self::Unknown,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BatchItem<D>
where
D: Clone + Send + Sync + Serialize,
{
pub id: String,
pub data: D,
pub status: BatchItemStatus,
pub error: Option<String>,
pub duration_ms: Option<u64>,
pub size_bucket: SizeBucket,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trace_ctx: Option<TraceCtx>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub attempt_id: Option<AttemptId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trial_id: Option<TrialId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BatchJob<D>
where
D: Clone + Send + Sync + Serialize,
{
pub id: String,
pub resource_key: String,
pub operation: String,
pub overwrite_policy: OverwritePolicy,
pub items: Vec<BatchItem<D>>,
pub status: BatchJobStatus,
pub created_at: String,
pub started_at: Option<String>,
pub completed_at: Option<String>,
pub reordered: bool,
pub reorder_note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BatchCompletionSummary {
pub job_id: String,
pub operation: String,
pub resource_key: String,
pub total: usize,
pub succeeded: usize,
pub failed: usize,
pub skipped: usize,
pub total_duration_ms: u64,
pub avg_duration_ms: u64,
}
#[derive(Debug, Clone)]
pub struct ItemResult {
pub success: bool,
pub output: Option<String>,
pub error: Option<String>,
}
impl ItemResult {
pub fn success() -> Self {
Self {
success: true,
output: None,
error: None,
}
}
pub fn success_with_output(output: String) -> Self {
Self {
success: true,
output: Some(output),
error: None,
}
}
pub fn failure(error: String) -> Self {
Self {
success: false,
output: None,
error: Some(error),
}
}
}