use crate::contracts::{JobContract, Schedule, ValidatedSchedule};
use crate::{CronError, CronResult};
use async_trait::async_trait;
use std::borrow::Cow;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
type RunnableFunc =
Arc<dyn Fn() -> Pin<Box<dyn Future<Output = CronResult<()>> + Send>> + Send + Sync>;
pub struct FnJob {
id: String,
name: String,
schedule: ValidatedSchedule,
func: RunnableFunc,
}
impl std::fmt::Debug for FnJob {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FnJob")
.field("id", &self.id)
.field("name", &self.name)
.field("schedule", &"<cron schedule>")
.field("func", &"<closure>")
.finish()
}
}
#[async_trait]
impl JobContract for FnJob {
async fn run(&self) -> CronResult<()> {
(self.func)().await
}
fn id(&self) -> Cow<'_, str> {
Cow::Borrowed(&self.id)
}
fn name(&self) -> Cow<'_, str> {
Cow::Borrowed(&self.name)
}
fn schedule(&self) -> &dyn Schedule {
&self.schedule
}
}
impl FnJob {
pub fn new<F, Fut>(
id: impl Into<String>,
name: impl Into<String>,
schedule_expr: &str,
func: F,
) -> CronResult<Self>
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future<Output = CronResult<()>> + Send + 'static,
{
Ok(Self {
id: id.into(),
name: name.into(),
schedule: ValidatedSchedule::parse(schedule_expr)?,
func: Arc::new(move || Box::pin(func())),
})
}
pub fn new_blocking<F>(
id: impl Into<String>,
name: impl Into<String>,
schedule_expr: &str,
func: F,
) -> CronResult<Self>
where
F: Fn() -> CronResult<()> + Send + Sync + 'static + Clone,
{
Ok(Self {
id: id.into(),
name: name.into(),
schedule: ValidatedSchedule::parse(schedule_expr)?,
func: Arc::new(move || {
let f = func.clone();
Box::pin(async move {
tokio::task::spawn_blocking(f)
.await
.map_err(CronError::JoinError)?
})
}),
})
}
}