Skip to main content

ops_rs/trigger/
engine.rs

1use crate::{DryContext, OpError, Trigger, TriggerRegistry, WetContext};
2use tracing::trace;
3
4/// The TriggerEngine manages predicates and runs actions when predicates are met
5pub struct TriggerEngine {
6    // Primary trigger registry contains triggers to be evaluated on each tick
7    primary_registery: TriggerRegistry,
8    // Secondary trigger registry for triggers that are only evaluated on demand
9    secondary_registery: TriggerRegistry,
10}
11
12impl std::fmt::Debug for TriggerEngine {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        let primary_trigger_count = self.primary_registery.list_names().len();
15        let secondary_trigger_count = self.secondary_registery.list_names().len();
16
17        f.debug_struct("TriggerEngine")
18            .field("primary_trigger_count", &primary_trigger_count)
19            .field("secondary_trigger_count", &secondary_trigger_count)
20            .finish()
21    }
22}
23
24impl TriggerEngine {
25    pub fn new() -> Self {
26        Self {
27            primary_registery: TriggerRegistry::new(),
28            secondary_registery: TriggerRegistry::new(),
29        }
30    }
31
32    /// Clock pulse - evaluate all predicates and run associated actions when/if they trigger
33    pub async fn tick(&self, dry: &mut DryContext, wet: &mut WetContext) -> Result<(), OpError> {
34        for trigger in self.primary_registery.spawn_all() {
35            let name = trigger.predicate().metadata().name.to_string();
36            let should = trigger.predicate().perform(dry, wet).await?;
37            if should {
38                for action in &trigger.actions() {
39                    // Execute each action op; fail hard on error
40                    action.perform(dry, wet).await.map_err(|e| {
41                        OpError::Trigger(format!(
42                            "Trigger action failed for predicate '{}': {}",
43                            name, e
44                        ))
45                    })?;
46                }
47            } else {
48                trace!("[TriggerEngine] Predicate '{}' not triggered", name);
49            }
50        }
51        Ok(())
52    }
53
54    pub fn spawn(&self, name: &str) -> std::result::Result<Box<dyn Trigger>, String> {
55        if self.primary_registery.is_set(name) {
56            self.primary_registery.spawn(name)
57        } else if self.secondary_registery.is_set(name) {
58            self.secondary_registery.spawn(name)
59        } else {
60            Err(format!("Trigger '{}' not found in either registry", name))
61        }
62    }
63
64    pub fn primary_registry(&self) -> &TriggerRegistry {
65        &self.primary_registery
66    }
67
68    pub fn primary_registry_mut(&mut self) -> &mut TriggerRegistry {
69        &mut self.primary_registery
70    }
71
72    pub fn secondary_registry(&self) -> &TriggerRegistry {
73        &self.secondary_registery
74    }
75
76    pub fn secondary_registry_mut(&mut self) -> &mut TriggerRegistry {
77        &mut self.secondary_registery
78    }
79}
80
81impl Default for TriggerEngine {
82    fn default() -> Self {
83        Self::new()
84    }
85}