Skip to main content

systemprompt_provider_contracts/
job.rs

1//! [`Job`] contract for scheduled / on-startup background jobs registered
2//! via the `inventory` crate.
3
4use std::collections::HashMap;
5use std::sync::Arc;
6
7use async_trait::async_trait;
8
9use crate::error::ProviderResult;
10
11#[derive(Debug, Clone)]
12pub struct JobResult {
13    pub success: bool,
14    pub message: Option<String>,
15    pub items_processed: Option<u64>,
16    pub items_failed: Option<u64>,
17    pub duration_ms: u64,
18}
19
20impl JobResult {
21    #[must_use]
22    pub const fn success() -> Self {
23        Self {
24            success: true,
25            message: None,
26            items_processed: None,
27            items_failed: None,
28            duration_ms: 0,
29        }
30    }
31
32    #[must_use]
33    pub fn with_message(mut self, message: impl Into<String>) -> Self {
34        self.message = Some(message.into());
35        self
36    }
37
38    #[must_use]
39    pub const fn with_stats(mut self, processed: u64, failed: u64) -> Self {
40        self.items_processed = Some(processed);
41        self.items_failed = Some(failed);
42        self
43    }
44
45    #[must_use]
46    pub const fn with_duration(mut self, duration_ms: u64) -> Self {
47        self.duration_ms = duration_ms;
48        self
49    }
50
51    #[must_use]
52    pub fn failure(message: impl Into<String>) -> Self {
53        Self {
54            success: false,
55            message: Some(message.into()),
56            items_processed: None,
57            items_failed: None,
58            duration_ms: 0,
59        }
60    }
61}
62
63pub struct JobContext {
64    db_pool: Arc<dyn std::any::Any + Send + Sync>,
65    app_context: Arc<dyn std::any::Any + Send + Sync>,
66    app_paths: Arc<dyn std::any::Any + Send + Sync>,
67    parameters: HashMap<String, String>,
68}
69
70impl std::fmt::Debug for JobContext {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        f.debug_struct("JobContext")
73            .field("db_pool", &"<type-erased>")
74            .field("app_context", &"<type-erased>")
75            .field("app_paths", &"<type-erased>")
76            .field("parameters", &self.parameters)
77            .finish()
78    }
79}
80
81impl JobContext {
82    #[must_use]
83    pub fn new(
84        db_pool: Arc<dyn std::any::Any + Send + Sync>,
85        app_context: Arc<dyn std::any::Any + Send + Sync>,
86        app_paths: Arc<dyn std::any::Any + Send + Sync>,
87    ) -> Self {
88        Self {
89            db_pool,
90            app_context,
91            app_paths,
92            parameters: HashMap::new(),
93        }
94    }
95
96    #[must_use]
97    pub fn with_parameters(mut self, parameters: HashMap<String, String>) -> Self {
98        self.parameters = parameters;
99        self
100    }
101
102    #[must_use]
103    pub fn db_pool<T: 'static>(&self) -> Option<&T> {
104        self.db_pool.as_ref().downcast_ref::<T>()
105    }
106
107    #[must_use]
108    pub fn app_context<T: 'static>(&self) -> Option<&T> {
109        self.app_context.as_ref().downcast_ref::<T>()
110    }
111
112    #[must_use]
113    pub fn app_paths<T: 'static>(&self) -> Option<&T> {
114        self.app_paths.as_ref().downcast_ref::<T>()
115    }
116
117    #[must_use]
118    pub fn db_pool_arc(&self) -> Arc<dyn std::any::Any + Send + Sync> {
119        Arc::clone(&self.db_pool)
120    }
121
122    #[must_use]
123    pub fn app_context_arc(&self) -> Arc<dyn std::any::Any + Send + Sync> {
124        Arc::clone(&self.app_context)
125    }
126
127    #[must_use]
128    pub fn app_paths_arc(&self) -> Arc<dyn std::any::Any + Send + Sync> {
129        Arc::clone(&self.app_paths)
130    }
131
132    #[must_use]
133    pub const fn parameters(&self) -> &HashMap<String, String> {
134        &self.parameters
135    }
136
137    #[must_use]
138    pub fn get_parameter(&self, key: &str) -> Option<&String> {
139        self.parameters.get(key)
140    }
141}
142
143#[async_trait]
144pub trait Job: Send + Sync + 'static {
145    fn name(&self) -> &'static str;
146
147    fn description(&self) -> &'static str {
148        ""
149    }
150
151    fn schedule(&self) -> &'static str;
152
153    fn tags(&self) -> Vec<&'static str> {
154        vec![]
155    }
156
157    async fn execute(&self, ctx: &JobContext) -> ProviderResult<JobResult>;
158
159    fn enabled(&self) -> bool {
160        true
161    }
162
163    fn run_on_startup(&self) -> bool {
164        false
165    }
166}
167
168inventory::collect!(&'static dyn Job);
169
170#[macro_export]
171macro_rules! submit_job {
172    ($job:expr) => {
173        inventory::submit!($job as &'static dyn $crate::Job);
174    };
175}