use std::time::{Duration, SystemTime};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
common::{Label, LabelFilter, TimeRange},
executions::{ExecutionDetails, ExecutionId, ExecutionStatus},
executors::ExecutorId,
schedules::ScheduleId,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(transparent)]
pub struct JobId(pub Uuid);
impl std::fmt::Display for JobId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<Uuid> for JobId {
fn from(value: Uuid) -> Self {
Self(value)
}
}
impl From<JobId> for Uuid {
fn from(value: JobId) -> Self {
value.0
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(transparent)]
#[serde(transparent)]
pub struct JobTypeId(String);
impl std::fmt::Display for JobTypeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl JobTypeId {
pub fn new<S: Into<String>>(s: S) -> Result<Self, InvalidJobTypeId> {
let s = s.into();
if !Self::validate(&s) {
return Err(InvalidJobTypeId(s));
}
Ok(Self(s))
}
pub fn new_unchecked<S: Into<String>>(s: S) -> Self {
Self(s.into())
}
fn validate(s: &str) -> bool {
if s.is_empty() {
return false;
}
let segments = s.split('.');
for segment in segments {
if segment.is_empty() {
return false;
}
let mut chars = segment.bytes();
match chars.next() {
Some(c) if c.is_ascii_alphabetic() => {}
_ => return false,
}
for c in chars {
if !(c.is_ascii_alphanumeric() || c == b'_') {
return false;
}
}
}
true
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn into_inner(self) -> String {
self.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvalidJobTypeId(pub String);
impl std::fmt::Display for InvalidJobTypeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, r#"invalid job type ID: "{}""#, self.0)
}
}
impl core::error::Error for InvalidJobTypeId {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobType {
pub id: JobTypeId,
pub description: Option<String>,
pub input_schema_json: Option<String>,
pub output_schema_json: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobDefinition {
pub job_type_id: JobTypeId,
pub target_execution_time: SystemTime,
pub input_payload_json: String,
pub labels: Vec<Label>,
pub timeout_policy: TimeoutPolicy,
pub retry_policy: RetryPolicy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobDetails {
pub id: JobId,
pub job: JobDefinition,
pub created_at: SystemTime,
pub executions: Vec<ExecutionDetails>,
pub schedule_id: Option<ScheduleId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeoutPolicy {
pub timeout: Duration,
pub base_time: TimeoutBaseTime,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TimeoutBaseTime {
TargetExecutionTime,
StartTime,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct RetryPolicy {
pub retries: u64,
#[serde(default)]
pub backoff_duration: Duration,
#[serde(default)]
pub max_backoff_duration: Option<Duration>,
#[serde(default)]
pub backoff_strategy: BackoffStrategy,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub enum BackoffStrategy {
#[default]
Fixed,
Exponential,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct JobFilters {
pub job_ids: Option<Vec<JobId>>,
pub job_type_ids: Option<Vec<JobTypeId>>,
pub executor_ids: Option<Vec<ExecutorId>>,
pub execution_ids: Option<Vec<ExecutionId>>,
pub execution_statuses: Option<Vec<ExecutionStatus>>,
pub target_execution_time: Option<TimeRange>,
pub created_at: Option<TimeRange>,
pub labels: Option<Vec<LabelFilter>>,
pub schedule_ids: Option<Vec<ScheduleId>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum JobOrderBy {
TargetExecutionTimeAsc,
TargetExecutionTimeDesc,
CreatedAtAsc,
CreatedAtDesc,
}
pub struct NewJob {
pub job: JobDefinition,
pub schedule_id: Option<ScheduleId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CancelledJob {
pub job_id: JobId,
pub last_execution_id: ExecutionId,
}
pub enum AddedJobs {
Added(Vec<JobId>),
Existing(Vec<JobId>),
}
impl AddedJobs {
#[must_use]
pub fn job_ids(&self) -> &[JobId] {
match self {
AddedJobs::Added(ids) | AddedJobs::Existing(ids) => ids,
}
}
#[must_use]
pub fn added_job_ids(&self) -> &[JobId] {
match self {
AddedJobs::Added(ids) => ids,
AddedJobs::Existing(_) => &[],
}
}
}