use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum QueuePriority {
Low,
Normal,
High,
}
impl QueuePriority {
pub fn as_i32(&self) -> i32 {
match self {
QueuePriority::Low => 3,
QueuePriority::Normal => 2,
QueuePriority::High => 1,
}
}
pub fn from_i32(val: i32) -> Self {
match val {
1 => QueuePriority::High,
2 => QueuePriority::Normal,
_ => QueuePriority::Low,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum QueueJobStatus {
Pending,
Processing,
Completed,
Failed,
Cancelled,
}
impl QueueJobStatus {
pub fn as_str(&self) -> &str {
match self {
QueueJobStatus::Pending => "pending",
QueueJobStatus::Processing => "processing",
QueueJobStatus::Completed => "completed",
QueueJobStatus::Failed => "failed",
QueueJobStatus::Cancelled => "cancelled",
}
}
pub fn parse(s: &str) -> Option<Self> {
match s {
"pending" => Some(QueueJobStatus::Pending),
"processing" => Some(QueueJobStatus::Processing),
"completed" => Some(QueueJobStatus::Completed),
"failed" => Some(QueueJobStatus::Failed),
"cancelled" => Some(QueueJobStatus::Cancelled),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound(deserialize = "T: DeserializeOwned"))]
pub struct QueueJob<T>
where
T: Serialize + DeserializeOwned + Clone + Send + Sync,
{
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trace_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trace_ctx: Option<stack_ids::TraceCtx>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub attempt_id: Option<stack_ids::AttemptId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trial_id: Option<stack_ids::TrialId>,
pub priority: QueuePriority,
pub status: QueueJobStatus,
pub data: T,
pub created_at: Option<String>,
pub started_at: Option<String>,
pub completed_at: Option<String>,
pub error_message: Option<String>,
}
impl<T> QueueJob<T>
where
T: Serialize + DeserializeOwned + Clone + Send + Sync,
{
pub fn new(data: T) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
trace_id: None,
trace_ctx: None,
attempt_id: Some(stack_ids::AttemptId::generate()),
trial_id: None,
priority: QueuePriority::Normal,
status: QueueJobStatus::Pending,
data,
created_at: None,
started_at: None,
completed_at: None,
error_message: None,
}
}
pub fn with_priority(mut self, priority: QueuePriority) -> Self {
self.priority = priority;
self
}
pub fn with_id(mut self, id: String) -> Self {
self.id = id;
self
}
pub fn with_trace_id(mut self, trace_id: impl Into<String>) -> Self {
let id = trace_id.into();
self.trace_ctx = Some(stack_ids::TraceCtx::from_legacy_trace_id(&id));
self.trace_id = Some(id);
self
}
pub fn with_trace_ctx(mut self, ctx: stack_ids::TraceCtx) -> Self {
self.trace_id = Some(ctx.trace_id.clone());
self.trace_ctx = Some(ctx);
self
}
pub fn with_attempt_id(mut self, attempt_id: stack_ids::AttemptId) -> Self {
self.attempt_id = Some(attempt_id);
self
}
pub fn with_trial_id(mut self, trial_id: stack_ids::TrialId) -> Self {
self.trial_id = Some(trial_id);
self
}
pub fn resolve_trace_ctx(&self) -> Option<stack_ids::TraceCtx> {
self.trace_ctx.clone().or_else(|| {
self.trace_id
.as_ref()
.map(stack_ids::TraceCtx::from_legacy_trace_id)
})
}
pub fn trace_ctx_compat(&self) -> Option<stack_ids::TraceCtx> {
self.resolve_trace_ctx()
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct QueueStats {
pub pending: u32,
pub processing: u32,
pub completed: u32,
pub failed: u32,
pub cancelled: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FailureClass {
Transient,
Permanent,
RateLimited { retry_after_secs: u64 },
}
impl FailureClass {
pub fn as_str(&self) -> &str {
match self {
Self::Transient => "transient",
Self::Permanent => "permanent",
Self::RateLimited { .. } => "rate_limited",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobResult {
pub success: bool,
pub output: Option<String>,
pub error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub failure_class: Option<FailureClass>,
}
impl JobResult {
pub fn success() -> Self {
Self {
success: true,
output: None,
error: None,
failure_class: None,
}
}
pub fn success_with_output(output: String) -> Self {
Self {
success: true,
output: Some(output),
error: None,
failure_class: None,
}
}
pub fn failure(error: String) -> Self {
Self {
success: false,
output: None,
error: Some(error),
failure_class: Some(FailureClass::Permanent),
}
}
pub fn with_failure_class(mut self, failure_class: FailureClass) -> Self {
self.failure_class = Some(failure_class);
self
}
pub fn transient_failure(error: String) -> Self {
Self::failure(error).with_failure_class(FailureClass::Transient)
}
pub fn rate_limited(error: String, retry_after_secs: u64) -> Self {
Self::failure(error).with_failure_class(FailureClass::RateLimited { retry_after_secs })
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct QueueJobDetails {
pub id: String,
pub priority: QueuePriority,
pub status: QueueJobStatus,
pub data_json: String,
pub trace_id: Option<String>,
pub created_at: Option<String>,
pub started_at: Option<String>,
pub completed_at: Option<String>,
pub error_message: Option<String>,
pub worker_id: Option<String>,
pub heartbeat_at: Option<String>,
pub visibility_timeout_secs: u64,
pub failure_class: Option<String>,
pub next_run_at: Option<String>,
pub attempt_count: u32,
pub attempt_id: Option<String>,
pub trial_id: Option<String>,
}