use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, SystemTime};
use cronoq_core::{LeaseId, Tags, TaskId};
use tokio_util::sync::CancellationToken;
use crate::error::TaskError;
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
pub trait Executor<P, O>: Send + Sync + 'static {
fn execute(&self, ctx: TaskContext, payload: Arc<P>) -> BoxFuture<Result<O, TaskError>>;
fn abort(&self, _lease: LeaseId) {}
}
pub struct FnExecutor<F>(F);
impl<F> FnExecutor<F> {
pub fn new(f: F) -> Self {
FnExecutor(f)
}
}
impl<P, O, F, Fut> Executor<P, O> for FnExecutor<F>
where
F: Fn(TaskContext, Arc<P>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<O, TaskError>> + Send + 'static,
{
fn execute(&self, ctx: TaskContext, payload: Arc<P>) -> BoxFuture<Result<O, TaskError>> {
Box::pin((self.0)(ctx, payload))
}
}
pub(crate) type HeartbeatFn = Arc<dyn Fn(LeaseId, Arc<AtomicBool>) + Send + Sync>;
#[derive(Clone)]
pub struct TaskContext {
pub id: TaskId,
pub lease: LeaseId,
pub attempt: u32,
pub pool: Arc<str>,
pub kind: Arc<str>,
pub key: Option<Arc<str>>,
pub tags: Tags,
pub deadline: Option<SystemTime>,
pub timeout: Option<Duration>,
pub group: cronoq_core::Labels,
pub(crate) cancel: CancellationToken,
pub(crate) heartbeat: HeartbeatFn,
pub(crate) heartbeat_pending: Arc<AtomicBool>,
}
impl std::fmt::Debug for TaskContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TaskContext")
.field("id", &self.id)
.field("attempt", &self.attempt)
.field("pool", &self.pool)
.field("kind", &self.kind)
.field("key", &self.key)
.finish_non_exhaustive()
}
}
impl TaskContext {
pub fn for_test(id: TaskId) -> TaskContext {
TaskContext {
id,
lease: LeaseId(id.0),
attempt: 1,
pool: cronoq_core::default_name(),
kind: cronoq_core::default_name(),
key: None,
tags: Arc::from(Vec::new()),
deadline: None,
timeout: None,
group: Arc::from(Vec::new()),
cancel: CancellationToken::new(),
heartbeat: Arc::new(|_, _| {}),
heartbeat_pending: Arc::new(AtomicBool::new(false)),
}
}
pub fn is_cancelled(&self) -> bool {
self.cancel.is_cancelled()
}
pub async fn cancelled(&self) {
self.cancel.cancelled().await
}
pub fn cancellation_token(&self) -> CancellationToken {
self.cancel.clone()
}
pub fn heartbeat(&self) {
if !self.heartbeat_pending.swap(true, Ordering::AcqRel) {
(self.heartbeat)(self.lease, self.heartbeat_pending.clone());
}
}
pub fn tag(&self, key: &str) -> Option<&str> {
self.tags
.iter()
.find(|(k, _)| &**k == key)
.map(|(_, v)| &**v)
}
pub fn traceparent(&self) -> Option<&str> {
self.tag("traceparent")
}
}