Skip to main content

trailbase_wasm/
job.rs

1use futures_util::future::LocalBoxFuture;
2use wstd::http::server::{Finished, Responder};
3
4use crate::http::IntoResponse;
5
6#[derive(Debug, thiserror::Error)]
7pub enum Error {
8  #[error("InvalidSpec")]
9  InvalidSpec,
10}
11
12pub type JobHandler = Box<dyn Fn(Responder) -> LocalBoxFuture<'static, Finished>>;
13
14pub struct Job {
15  pub name: String,
16  pub spec: String,
17  pub handler: JobHandler,
18}
19
20impl Job {
21  pub fn new<F, R, B>(
22    name: impl std::string::ToString,
23    spec: impl std::string::ToString,
24    f: F,
25  ) -> Result<Self, Error>
26  where
27    F: (AsyncFn() -> R) + Send + Sync + 'static,
28    R: IntoResponse<B>,
29    B: wstd::http::body::Body,
30  {
31    let spec = spec.to_string();
32    validate_spec(&spec)?;
33
34    let f = std::rc::Rc::new(f);
35
36    return Ok(Self {
37      name: name.to_string(),
38      spec,
39      handler: Box::new(move |responder| {
40        let f = f.clone();
41        Box::pin(async move { responder.respond(f().await.into_response()).await })
42      }),
43    });
44  }
45
46  pub fn minutely<F, R, B>(name: impl std::string::ToString, f: F) -> Self
47  where
48    F: (AsyncFn() -> R) + Send + Sync + 'static,
49    R: IntoResponse<B>,
50    B: wstd::http::body::Body,
51  {
52    return Self::new(name, "37 * * * * *", f).expect("valid spec");
53  }
54
55  pub fn hourly<F, R, B>(name: impl std::string::ToString, f: F) -> Self
56  where
57    F: (AsyncFn() -> R) + Send + Sync + 'static,
58    R: IntoResponse<B>,
59    B: wstd::http::body::Body,
60  {
61    return Self::new(name, "@hourly", f).expect("valid spec");
62  }
63
64  pub fn daily<F, R, B>(name: impl std::string::ToString, f: F) -> Self
65  where
66    F: (AsyncFn() -> R) + Send + Sync + 'static,
67    R: IntoResponse<B>,
68    B: wstd::http::body::Body,
69  {
70    return Self::new(name, "@daily", f).expect("valid spec");
71  }
72}
73
74fn validate_spec(spec: &str) -> Result<(), Error> {
75  return match spec {
76    "@hourly" | "@daily" | "@weekly" | "@monthly" | "@yearly" => Ok(()),
77    spec => match spec.trim().split(" ").count() {
78      6 | 7 => Ok(()),
79      _ => Err(Error::InvalidSpec),
80    },
81  };
82}