use crate::{Uuid, datetime::DateTime, error::Error};
use std::{
any::Any,
time::{Duration, Instant},
};
#[derive(Debug)]
pub struct JobContext {
job_id: Uuid,
job_name: Option<&'static str>,
source: String,
start_time: Instant,
disabled: bool,
immediate: bool,
remaining_ticks: Option<usize>,
last_tick: Option<DateTime>,
next_tick: Option<DateTime>,
execution_error: Option<Error>,
job_data: Option<Box<dyn Any + Send>>,
}
impl JobContext {
#[inline]
pub fn new() -> Self {
Self {
job_id: Uuid::now_v7(),
job_name: None,
source: String::new(),
start_time: Instant::now(),
disabled: false,
immediate: false,
remaining_ticks: None,
last_tick: None,
next_tick: None,
execution_error: None,
job_data: None,
}
}
#[inline]
pub fn start(&mut self) {
self.start_time = Instant::now();
}
pub fn finish(&mut self) {
if let Some(ticks) = self.remaining_ticks {
self.remaining_ticks = Some(ticks.saturating_sub(1));
}
let job_id = self.job_id.to_string();
let job_name = self.job_name;
let remaining_ticks = self.remaining_ticks;
let last_tick = self.last_tick.map(|dt| dt.to_string());
let next_tick = self.next_tick.map(|dt| dt.to_string());
let execution_time = self.start_time.elapsed();
let execution_time_millis = execution_time.as_millis();
if let Some(error) = self.execution_error.as_ref() {
tracing::error!(
job_id,
job_name,
remaining_ticks,
last_tick,
next_tick,
execution_time_millis,
"{error}"
);
} else {
tracing::warn!(
job_id,
job_name,
remaining_ticks,
last_tick,
next_tick,
execution_time_millis,
);
}
#[cfg(feature = "metrics")]
if let Some(name) = job_name {
metrics::histogram!(
"zino_job_execution_duration_seconds",
"job_name" => name,
)
.record(execution_time.as_secs_f64());
} else {
metrics::histogram!(
"zino_job_execution_duration_seconds",
"job_id" => job_id,
)
.record(execution_time.as_secs_f64());
}
self.set_last_tick(DateTime::now());
}
#[inline]
pub fn record_error(&mut self, error: impl Into<Error>) {
self.execution_error = Some(error.into());
}
#[inline]
pub fn retry_after(&mut self, duration: Duration) {
self.next_tick = Some(DateTime::now() + duration);
}
#[inline]
pub fn set_name(&mut self, name: &'static str) {
self.job_name = Some(name);
}
#[inline]
pub fn set_source(&mut self, source: impl Into<String>) {
self.source = source.into();
}
#[inline]
pub fn set_remaining_ticks(&mut self, ticks: usize) {
self.remaining_ticks = Some(ticks);
}
#[inline]
pub fn set_last_tick(&mut self, last_tick: DateTime) {
self.last_tick = Some(last_tick);
}
#[inline]
pub fn set_next_tick(&mut self, next_tick: Option<DateTime>) {
self.next_tick = next_tick;
}
#[inline]
pub fn set_disabled_status(&mut self, disabled: bool) {
self.disabled = disabled;
}
#[inline]
pub fn set_immediate_mode(&mut self, immediate: bool) {
self.immediate = immediate;
}
#[inline]
pub fn set_data<T: Send + 'static>(&mut self, data: T) {
self.job_data = Some(Box::new(data));
}
#[inline]
pub fn get_data<T: Send + 'static>(&self) -> Option<&T> {
self.job_data
.as_ref()
.and_then(|data| data.downcast_ref::<T>())
}
#[inline]
pub fn get_data_mut<T: Send + 'static>(&mut self) -> Option<&mut T> {
self.job_data
.as_mut()
.and_then(|data| data.downcast_mut::<T>())
}
#[inline]
pub fn take_data<T: Send + 'static>(&mut self) -> Option<Box<T>> {
self.job_data
.take()
.and_then(|data| data.downcast::<T>().ok())
}
#[inline]
pub fn job_id(&self) -> Uuid {
self.job_id
}
#[inline]
pub fn job_name(&self) -> Option<&'static str> {
self.job_name
}
#[inline]
pub fn source(&self) -> &str {
&self.source
}
#[inline]
pub fn start_time(&self) -> Instant {
self.start_time
}
#[inline]
pub fn is_disabled(&self) -> bool {
self.disabled
}
pub fn is_immediate(&self) -> bool {
self.immediate && self.last_tick.is_none()
|| self.next_tick.and_then(|dt| dt.span_before_now()).is_some()
}
#[inline]
pub fn is_fused(&self) -> bool {
self.remaining_ticks == Some(0)
}
#[inline]
pub fn last_tick(&self) -> Option<DateTime> {
self.last_tick
}
#[inline]
pub fn next_tick(&self) -> Option<DateTime> {
self.next_tick
}
#[inline]
pub fn execution_error(&self) -> Option<&Error> {
self.execution_error.as_ref()
}
}
impl Default for JobContext {
#[inline]
fn default() -> Self {
Self::new()
}
}