entelix_runnable/
router.rs1use std::sync::Arc;
8
9use entelix_core::{Error, ExecutionContext, Result};
10
11use crate::runnable::Runnable;
12
13type Predicate<I> = Arc<dyn Fn(&I) -> bool + Send + Sync>;
14type Branch<I, O> = (Predicate<I>, Arc<dyn Runnable<I, O>>);
15
16pub struct RunnableRouter<I, O>
19where
20 I: Send + 'static,
21 O: Send + 'static,
22{
23 routes: Vec<Branch<I, O>>,
24 fallback: Option<Arc<dyn Runnable<I, O>>>,
25}
26
27impl<I, O> RunnableRouter<I, O>
28where
29 I: Send + 'static,
30 O: Send + 'static,
31{
32 pub fn new() -> Self {
34 Self {
35 routes: Vec::new(),
36 fallback: None,
37 }
38 }
39
40 #[must_use]
43 pub fn route<F, R>(mut self, predicate: F, runnable: R) -> Self
44 where
45 F: Fn(&I) -> bool + Send + Sync + 'static,
46 R: Runnable<I, O> + 'static,
47 {
48 self.routes.push((Arc::new(predicate), Arc::new(runnable)));
49 self
50 }
51
52 #[must_use]
55 pub fn fallback<R>(mut self, runnable: R) -> Self
56 where
57 R: Runnable<I, O> + 'static,
58 {
59 self.fallback = Some(Arc::new(runnable));
60 self
61 }
62
63 pub fn len(&self) -> usize {
65 self.routes.len()
66 }
67
68 pub fn is_empty(&self) -> bool {
71 self.routes.is_empty()
72 }
73}
74
75impl<I, O> Default for RunnableRouter<I, O>
76where
77 I: Send + 'static,
78 O: Send + 'static,
79{
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85#[async_trait::async_trait]
86impl<I, O> Runnable<I, O> for RunnableRouter<I, O>
87where
88 I: Send + 'static,
89 O: Send + 'static,
90{
91 async fn invoke(&self, input: I, ctx: &ExecutionContext) -> Result<O> {
92 for (predicate, runnable) in &self.routes {
93 if predicate(&input) {
94 return runnable.invoke(input, ctx).await;
95 }
96 }
97 if let Some(fallback) = &self.fallback {
98 return fallback.invoke(input, ctx).await;
99 }
100 Err(Error::invalid_request(
101 "RunnableRouter: no route matched and no fallback was set",
102 ))
103 }
104}