Skip to main content

taktora_executor/
condition.rs

1//! `wrap_with_condition` — a helper that gates an [`ExecutableItem`] on a
2//! user-supplied predicate.
3
4use crate::context::Context;
5use crate::control_flow::{ControlFlow, ExecuteResult};
6use crate::error::ExecutorError;
7use crate::item::ExecutableItem;
8use crate::trigger::TriggerDeclarer;
9
10/// Wraps `item` so that, on each invocation, `cond()` runs first. If `cond`
11/// returns `false`, the wrapper returns `Ok(StopChain)` and `item.execute`
12/// is **not** called.
13///
14/// The wrapper forwards `declare_triggers` to the inner item, so triggers
15/// are inherited.
16pub const fn wrap_with_condition<I, F>(item: I, cond: F) -> Conditional<I, F>
17where
18    I: ExecutableItem,
19    F: FnMut() -> bool + Send + 'static,
20{
21    Conditional { item, cond }
22}
23
24/// Conditional wrapper produced by [`wrap_with_condition`].
25pub struct Conditional<I, F> {
26    item: I,
27    cond: F,
28}
29
30impl<I, F> ExecutableItem for Conditional<I, F>
31where
32    I: ExecutableItem,
33    F: FnMut() -> bool + Send + 'static,
34{
35    fn declare_triggers(&mut self, d: &mut TriggerDeclarer<'_>) -> Result<(), ExecutorError> {
36        self.item.declare_triggers(d)
37    }
38
39    fn execute(&mut self, ctx: &mut Context<'_>) -> ExecuteResult {
40        if (self.cond)() {
41            self.item.execute(ctx)
42        } else {
43            Ok(ControlFlow::StopChain)
44        }
45    }
46
47    fn task_id(&self) -> Option<&str> {
48        self.item.task_id()
49    }
50
51    fn app_id(&self) -> Option<u32> {
52        self.item.app_id()
53    }
54
55    fn app_instance_id(&self) -> Option<u32> {
56        self.item.app_instance_id()
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use crate::ControlFlow;
64    use crate::context::ContextHarness;
65    use crate::item::item;
66
67    #[test]
68    fn condition_true_runs_inner() {
69        let mut wrapped = wrap_with_condition(item(|_| Ok(ControlFlow::Continue)), || true);
70        let h = ContextHarness::new("t");
71        let res = wrapped.execute(&mut h.context()).unwrap();
72        assert_eq!(res, ControlFlow::Continue);
73    }
74
75    #[test]
76    fn condition_false_stops_chain() {
77        let mut wrapped = wrap_with_condition(item(|_| Ok(ControlFlow::Continue)), || false);
78        let h = ContextHarness::new("t");
79        let res = wrapped.execute(&mut h.context()).unwrap();
80        assert_eq!(res, ControlFlow::StopChain);
81    }
82}