aster_forge_tasks/registry.rs
1//! Product-facing task registry support traits.
2
3/// Minimal read-only view over a persisted task row.
4///
5/// Product crates implement this for their database model so Forge can decode typed payloads and
6/// results without depending on a concrete ORM entity or schema extension columns.
7pub trait TaskRecord<Kind> {
8 /// Stable database identifier used in diagnostics.
9 fn id(&self) -> i64;
10
11 /// Product-owned task kind enum.
12 fn kind(&self) -> Kind;
13
14 /// Stored JSON payload.
15 fn payload_json(&self) -> &str;
16
17 /// Stored JSON result, if the task has completed with one.
18 fn result_json(&self) -> Option<&str>;
19}
20
21/// Generates a product-local static task registry.
22///
23/// The macro intentionally requires an explicit `kind => static_adapter` mapping. That keeps task
24/// registration auditable in product crates while removing the repetitive match forwarding that
25/// otherwise drifts between `spec_for_kind`, `task_lane`, and `task_lane_kinds`.
26#[macro_export]
27macro_rules! task_registry {
28 (
29 $(#[$meta:meta])*
30 $vis:vis mod $module:ident {
31 state: $state:ty;
32 task: $task:ty;
33 config: $config:ty;
34 context: $context:ty;
35 error: $error:ty;
36 kind: $kind:ty;
37 lane: $lane:ty;
38 payload: $payload:ty;
39 result: $result:ty;
40 specs {
41 $(
42 $adapter:ident: $spec:ty => $kind_value:path
43 ),+ $(,)?
44 }
45 lanes {
46 $(
47 $lane_value:path => [$($lane_kind:path),* $(,)?]
48 ),+ $(,)?
49 }
50 }
51 ) => {
52 $(#[$meta])*
53 $vis mod $module {
54 $(
55 static $adapter: $crate::TaskSpecAdapter<$spec> =
56 $crate::TaskSpecAdapter::new();
57 )+
58
59 /// Returns the registered task spec for a product task kind.
60 pub fn spec_for_kind(
61 kind: $kind,
62 ) -> &'static dyn $crate::ErasedBackgroundTaskSpec<
63 $state,
64 $task,
65 $config,
66 $context,
67 $kind,
68 $lane,
69 $payload,
70 $result,
71 $error,
72 > {
73 match kind {
74 $(
75 $kind_value => &$adapter,
76 )+
77 }
78 }
79
80 /// Returns the lane for a product task kind.
81 pub fn task_lane(kind: $kind) -> $lane {
82 spec_for_kind(kind).lane()
83 }
84
85 /// Returns all task kinds configured for a lane.
86 pub fn task_lane_kinds(lane: $lane) -> &'static [$kind] {
87 match lane {
88 $(
89 $lane_value => &[$($lane_kind),*],
90 )+
91 }
92 }
93
94 #[cfg(test)]
95 mod registry_tests {
96 use super::*;
97
98 #[test]
99 fn registered_task_lanes_are_bidirectionally_consistent() {
100 $(
101 let lane = task_lane($kind_value);
102 assert!(
103 task_lane_kinds(lane).contains(&$kind_value),
104 "lane {lane:?} does not list task kind {:?}",
105 $kind_value
106 );
107 )+
108 }
109 }
110 }
111 };
112}