moduforge_rules_engine/
engine.rs1use std::future::Future;
2use std::sync::Arc;
3
4use crate::decision::Decision;
5use crate::handler::custom_node_adapter::{CustomNodeAdapter, NoopCustomNode};
6use crate::handler::graph::DecisionGraphResponse;
7use crate::loader::{
8 ClosureLoader, DecisionLoader, LoaderResponse, LoaderResult, NoopLoader,
9};
10use crate::model::DecisionContent;
11use crate::EvaluationError;
12use moduforge_rules_expression::variable::Variable;
13use moduforge_rules_expression::functions::custom::CustomFunctionRegistry;
14
15#[derive(Debug, Clone)]
17pub struct DecisionEngine<Loader, CustomNode>
18where
19 Loader: DecisionLoader + 'static,
20 CustomNode: CustomNodeAdapter + 'static,
21{
22 loader: Arc<Loader>,
23 adapter: Arc<CustomNode>,
24}
25
26#[derive(Debug, Default)]
27pub struct EvaluationOptions {
28 pub trace: Option<bool>,
29 pub max_depth: Option<u8>,
30}
31
32impl Default for DecisionEngine<NoopLoader, NoopCustomNode> {
33 fn default() -> Self {
34 Self {
35 loader: Arc::new(NoopLoader::default()),
36 adapter: Arc::new(NoopCustomNode::default()),
37 }
38 }
39}
40
41impl<L: DecisionLoader + 'static, A: CustomNodeAdapter + 'static>
42 DecisionEngine<L, A>
43{
44 pub fn new(
45 loader: Arc<L>,
46 adapter: Arc<A>,
47 ) -> Self {
48 Self { loader, adapter }
49 }
50
51 pub fn with_adapter<CustomNode>(
52 self,
53 adapter: Arc<CustomNode>,
54 ) -> DecisionEngine<L, CustomNode>
55 where
56 CustomNode: CustomNodeAdapter,
57 {
58 DecisionEngine { loader: self.loader, adapter }
59 }
60
61 pub fn with_loader<Loader>(
62 self,
63 loader: Arc<Loader>,
64 ) -> DecisionEngine<Loader, A>
65 where
66 Loader: DecisionLoader,
67 {
68 DecisionEngine { loader, adapter: self.adapter }
69 }
70
71 pub fn with_closure_loader<F, O>(
72 self,
73 loader: F,
74 ) -> DecisionEngine<ClosureLoader<F>, A>
75 where
76 F: Fn(String) -> O + Sync + Send,
77 O: Future<Output = LoaderResponse> + Send,
78 {
79 DecisionEngine {
80 loader: Arc::new(ClosureLoader::new(loader)),
81 adapter: self.adapter,
82 }
83 }
84
85 pub async fn evaluate<K>(
87 &self,
88 key: K,
89 context: Variable,
90 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
91 where
92 K: AsRef<str>,
93 {
94 self.evaluate_with_opts(key, context, Default::default()).await
95 }
96 pub async fn evaluate_with_state_and_opts<K>(
97 &self,
98 key: K,
99 context: Variable,
100 state: Arc<moduforge_state::State>,
101 options: EvaluationOptions,
102 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
103 where
104 K: AsRef<str>,
105 {
106 CustomFunctionRegistry::set_current_state(Some(state));
108
109 let result = self.evaluate_with_opts(key, context, options).await;
111
112 CustomFunctionRegistry::set_current_state(None);
114
115 result
116 }
117 pub async fn evaluate_with_state<K>(
119 &self,
120 key: K,
121 context: Variable,
122 state: Arc<moduforge_state::State>,
123 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
124 where
125 K: AsRef<str>,
126 {
127 CustomFunctionRegistry::set_current_state(Some(state));
129
130 let result = self.evaluate(key, context).await;
132
133 CustomFunctionRegistry::set_current_state(None);
135
136 result
137 }
138
139 pub async fn evaluate_with_opts<K>(
141 &self,
142 key: K,
143 context: Variable,
144 options: EvaluationOptions,
145 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
146 where
147 K: AsRef<str>,
148 {
149 let content = self.loader.load(key.as_ref()).await?;
150 let decision = self.create_decision(content);
151 decision.evaluate_with_opts(context, options).await
152 }
153
154 pub fn create_decision(
156 &self,
157 content: Arc<DecisionContent>,
158 ) -> Decision<L, A> {
159 Decision::from(content)
160 .with_loader(self.loader.clone())
161 .with_adapter(self.adapter.clone())
162 }
163
164 pub async fn get_decision(
166 &self,
167 key: &str,
168 ) -> LoaderResult<Decision<L, A>> {
169 let content = self.loader.load(key).await?;
170 Ok(self.create_decision(content))
171 }
172
173 pub fn loader(&self) -> &L {
174 self.loader.as_ref()
175 }
176
177 pub fn adapter(&self) -> &A {
178 self.adapter.as_ref()
179 }
180}