termivar_scanner/decision_runner/
registry.rs1use super::{Arc, BTreeMap, DecisionActionExecutor, DecisionExecutionStage, DecisionRunnerError};
4
5#[derive(Clone, Default)]
7pub struct DecisionExecutorRegistry {
8 executors: BTreeMap<String, Arc<dyn DecisionActionExecutor>>,
9 routes: BTreeMap<(DecisionExecutionStage, String), String>,
10}
11
12impl DecisionExecutorRegistry {
13 pub fn new() -> Self {
15 Self::default()
16 }
17
18 pub fn register(
20 &mut self,
21 executor: Arc<dyn DecisionActionExecutor>,
22 ) -> Result<(), DecisionRunnerError> {
23 let id = non_empty(executor.id(), "executor id")?;
24 if self.executors.contains_key(&id) {
25 return Err(DecisionRunnerError::ExecutorIdentityConflict { executor_id: id });
26 }
27 self.executors.insert(id, executor);
28 Ok(())
29 }
30
31 pub fn route_action(
38 &mut self,
39 stage: DecisionExecutionStage,
40 action_id: impl Into<String>,
41 executor_id: impl Into<String>,
42 ) -> Result<(), DecisionRunnerError> {
43 let action_id = non_empty(action_id, "action id")?;
44 let executor_id = non_empty(executor_id, "executor id")?;
45 if !self.executors.contains_key(&executor_id) {
46 return Err(DecisionRunnerError::UnknownExecutor { executor_id });
47 }
48
49 let key = (stage, action_id.clone());
50 if let Some(existing) = self.routes.get(&key) {
51 return if existing == &executor_id {
52 Ok(())
53 } else {
54 Err(DecisionRunnerError::ActionRouteConflict { stage, action_id })
55 };
56 }
57 self.routes.insert(key, executor_id);
58 Ok(())
59 }
60
61 pub fn contains(&self, executor_id: &str) -> bool {
63 self.executors.contains_key(executor_id)
64 }
65
66 pub fn len(&self) -> usize {
68 self.executors.len()
69 }
70
71 pub fn is_empty(&self) -> bool {
73 self.executors.is_empty()
74 }
75
76 pub(super) fn resolve(
77 &self,
78 stage: DecisionExecutionStage,
79 action_id: &str,
80 requested_executor: Option<&str>,
81 ) -> Result<(String, Arc<dyn DecisionActionExecutor>), DecisionRunnerError> {
82 let executor_id = if let Some(requested) = requested_executor {
83 non_empty(requested, "executor id")?
84 } else {
85 self.routes
86 .get(&(stage, action_id.to_owned()))
87 .cloned()
88 .ok_or_else(|| DecisionRunnerError::MissingActionRoute {
89 stage,
90 action_id: action_id.to_owned(),
91 })?
92 };
93 let executor = self.executors.get(&executor_id).cloned().ok_or_else(|| {
94 DecisionRunnerError::UnknownExecutor {
95 executor_id: executor_id.clone(),
96 }
97 })?;
98 Ok((executor_id, executor))
99 }
100}
101
102fn non_empty(value: impl Into<String>, field: &'static str) -> Result<String, DecisionRunnerError> {
103 let value = value.into();
104 if value.trim().is_empty() {
105 return Err(DecisionRunnerError::EmptyValue { field });
106 }
107 Ok(value)
108}