actionqueue_core/subscription.rs
1//! Subscription domain types for event-driven task promotion.
2//!
3//! Subscriptions allow tasks to be promoted to Ready when specific events
4//! occur — task completion, run state changes, budget threshold crossings,
5//! or application-defined custom events. The dispatch loop evaluates active
6//! subscriptions each tick and triggers those whose filters match.
7
8use std::fmt::{Display, Formatter};
9use std::str::FromStr;
10
11use uuid::Uuid;
12
13use crate::budget::BudgetDimension;
14use crate::ids::TaskId;
15use crate::run::state::RunState;
16
17/// A unique identifier for an event subscription.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct SubscriptionId(Uuid);
21
22impl SubscriptionId {
23 /// Creates a new random SubscriptionId.
24 pub fn new() -> Self {
25 SubscriptionId(Uuid::new_v4())
26 }
27
28 /// Creates a SubscriptionId from a UUID.
29 pub fn from_uuid(uuid: Uuid) -> Self {
30 SubscriptionId(uuid)
31 }
32
33 /// Returns the inner UUID.
34 pub fn as_uuid(&self) -> &Uuid {
35 &self.0
36 }
37}
38
39impl Default for SubscriptionId {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl FromStr for SubscriptionId {
46 type Err = uuid::Error;
47
48 fn from_str(s: &str) -> Result<Self, Self::Err> {
49 Uuid::from_str(s).map(SubscriptionId)
50 }
51}
52
53impl Display for SubscriptionId {
54 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
55 write!(f, "{}", self.0)
56 }
57}
58
59/// A filter that specifies which events trigger a subscription.
60///
61/// Subscriptions are evaluated each tick against the set of events that
62/// occurred during that tick. A matching subscription triggers the
63/// subscribing task's scheduled run to be promoted to Ready.
64#[derive(Debug, Clone, PartialEq, Eq)]
65#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66pub enum EventFilter {
67 /// Fires when a specific task reaches terminal success (Completed).
68 TaskCompleted {
69 /// The task that must complete to trigger this subscription.
70 task_id: TaskId,
71 },
72 /// Fires when any run of a specific task transitions to the given state.
73 RunStateChanged {
74 /// The task to observe.
75 task_id: TaskId,
76 /// The target run state that triggers this subscription.
77 state: RunState,
78 },
79 /// Fires when a task's budget consumption crosses a percentage threshold.
80 BudgetThreshold {
81 /// The task whose budget is observed.
82 task_id: TaskId,
83 /// The budget dimension to observe.
84 dimension: BudgetDimension,
85 /// The consumption percentage (0-100) at which to trigger.
86 threshold_pct: u8,
87 },
88 /// Fires when an application-defined event with this key is emitted.
89 Custom {
90 /// The application-defined event key.
91 key: String,
92 },
93}