1use std::sync::Arc;
2
3use crate::error::{Error, Result};
4use crate::service::{RegistrySnapshot, Service};
5
6use super::meta::Meta;
7
8pub struct CronContext {
16 pub(crate) registry: Arc<RegistrySnapshot>,
17 pub(crate) meta: Meta,
18}
19
20pub trait FromCronContext: Sized {
25 fn from_cron_context(ctx: &CronContext) -> Result<Self>;
32}
33
34impl<T: Send + Sync + 'static> FromCronContext for Service<T> {
35 fn from_cron_context(ctx: &CronContext) -> Result<Self> {
36 ctx.registry.get::<T>().map(Service).ok_or_else(|| {
37 Error::internal(format!(
38 "service not found in registry: {}",
39 std::any::type_name::<T>()
40 ))
41 })
42 }
43}
44
45impl FromCronContext for Meta {
46 fn from_cron_context(ctx: &CronContext) -> Result<Self> {
47 Ok(ctx.meta.clone())
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use std::any::{Any, TypeId};
55 use std::collections::HashMap;
56
57 fn test_context() -> CronContext {
58 let mut services: HashMap<TypeId, Arc<dyn Any + Send + Sync>> = HashMap::new();
59 services.insert(TypeId::of::<u32>(), Arc::new(42u32));
60 let snapshot = Arc::new(RegistrySnapshot::new(services));
61
62 CronContext {
63 registry: snapshot,
64 meta: Meta {
65 name: "test_job".to_string(),
66 deadline: None,
67 tick: chrono::Utc::now(),
68 },
69 }
70 }
71
72 #[test]
73 fn service_extractor_finds_registered() {
74 let ctx = test_context();
75 let svc = Service::<u32>::from_cron_context(&ctx).unwrap();
76 assert_eq!(*svc.0, 42);
77 }
78
79 #[test]
80 fn service_extractor_fails_for_missing() {
81 let ctx = test_context();
82 let result = Service::<String>::from_cron_context(&ctx);
83 assert!(result.is_err());
84 }
85
86 #[test]
87 fn meta_extractor_returns_meta() {
88 let ctx = test_context();
89 let meta = Meta::from_cron_context(&ctx).unwrap();
90 assert_eq!(meta.name, "test_job");
91 }
92}