Skip to main content

chronon_core/
handle.rs

1//! Typed script handle for job scheduling.
2
3use std::marker::PhantomData;
4
5use serde::Serialize;
6
7use crate::models::Job;
8use crate::Result;
9
10/// A typed handle for scheduling a script with specific parameters.
11///
12/// Created by the `#[chronon::script]` macro. The attribute turns the annotated
13/// function into a handle factory (`fn nightly_cleanup() -> ScriptHandle<…>`) and
14/// moves the body to an internal `__*_impl` entry point used by the executor.
15///
16/// **Preferred scheduling:** build jobs with Chronon's fluent `JobBuilder`
17/// (`chronon_scheduler::JobBuilder`, re-exported from the `chronon` / `uf-chronon` facade)
18/// from this handle, then upsert via `CoordinatorService` or `RemoteCoordinatorClient`.
19/// [`Self::job`] / [`Self::job_with_params`] only seed `script_name` / `params_json` — a
20/// low-level alternate to the fluent builder.
21///
22/// # Examples
23///
24/// Handle identity (construction lives on Chronon `JobBuilder` — see
25/// `cargo run -p uf-chronon --example script_handle_job --features mem`):
26///
27/// ```
28/// use chronon_core::ScriptHandle;
29/// use serde::Serialize;
30///
31/// #[derive(Serialize)]
32/// struct NightlyCleanupParams {
33///     retention_days: u32,
34/// }
35///
36/// let handle = ScriptHandle::<NightlyCleanupParams>::new("nightly_cleanup");
37/// assert_eq!(handle.name(), "nightly_cleanup");
38/// ```
39///
40/// Low-level seed (prefer `JobBuilder::params` in application code):
41///
42/// ```
43/// use chronon_core::{Job, ScriptHandle};
44/// use serde::Serialize;
45///
46/// #[derive(Serialize)]
47/// struct NightlyCleanupParams {
48///     retention_days: u32,
49/// }
50///
51/// let handle = ScriptHandle::<NightlyCleanupParams>::new("nightly_cleanup");
52/// let job: Job = handle
53///     .job_with_params(
54///         "nightly-job",
55///         &NightlyCleanupParams {
56///             retention_days: 7,
57///         },
58///     )
59///     .expect("params serialize");
60/// assert_eq!(job.script_name, "nightly_cleanup");
61/// assert_eq!(job.params_json["retention_days"], 7);
62/// ```
63#[derive(Debug, Clone)]
64pub struct ScriptHandle<P> {
65    name: &'static str,
66    _params: PhantomData<P>,
67}
68
69impl<P> ScriptHandle<P> {
70    /// Create a new script handle (typically called by macro-generated code).
71    pub const fn new(name: &'static str) -> Self {
72        Self {
73            name,
74            _params: PhantomData,
75        }
76    }
77
78    /// Stable script registry name.
79    pub const fn name(&self) -> &'static str {
80        self.name
81    }
82
83    /// Baseline [`Job`] pointing at this script (`Job::new` defaults).
84    ///
85    /// Prefer Chronon `JobBuilder` (`chronon_scheduler::JobBuilder`) for cron / run-once /
86    /// manual scheduling. This method only seeds `job_name` and `script_name`.
87    pub fn job(&self, job_name: impl Into<String>) -> Job {
88        Job::new(job_name, self.name)
89    }
90}
91
92impl<P: Serialize> ScriptHandle<P> {
93    /// Baseline [`Job`] with typed params serialized into `params_json`.
94    ///
95    /// Prefer Chronon `JobBuilder::params` for fluent construction.
96    pub fn job_with_params(&self, job_name: impl Into<String>, params: &P) -> Result<Job> {
97        let mut job = self.job(job_name);
98        job.params_json = serde_json::to_value(params).map_err(crate::ChrononError::from)?;
99        Ok(job)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use serde::Serialize;
107
108    #[derive(Serialize)]
109    struct DemoParams {
110        n: u32,
111    }
112
113    #[test]
114    fn job_seeds_script_name() {
115        let handle = ScriptHandle::<()>::new("demo");
116        let job = handle.job("demo-job");
117        assert_eq!(job.script_name, "demo");
118        assert_eq!(job.job_name, "demo-job");
119    }
120
121    #[test]
122    fn job_with_params_serializes() {
123        let handle = ScriptHandle::<DemoParams>::new("demo");
124        let job = handle
125            .job_with_params("demo-job", &DemoParams { n: 3 })
126            .expect("serialize");
127        assert_eq!(job.params_json["n"], 3);
128    }
129}