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;
13
14#[derive(Debug, Clone)]
16pub struct DecisionEngine<Loader, CustomNode>
17where
18 Loader: DecisionLoader + 'static,
19 CustomNode: CustomNodeAdapter + 'static,
20{
21 loader: Arc<Loader>,
22 adapter: Arc<CustomNode>,
23}
24
25#[derive(Debug, Default)]
26pub struct EvaluationOptions {
27 pub trace: Option<bool>,
28 pub max_depth: Option<u8>,
29}
30
31impl Default for DecisionEngine<NoopLoader, NoopCustomNode> {
32 fn default() -> Self {
33 Self {
34 loader: Arc::new(NoopLoader::default()),
35 adapter: Arc::new(NoopCustomNode::default()),
36 }
37 }
38}
39
40impl<L: DecisionLoader + 'static, A: CustomNodeAdapter + 'static>
41 DecisionEngine<L, A>
42{
43 pub fn new(
44 loader: Arc<L>,
45 adapter: Arc<A>,
46 ) -> Self {
47 Self { loader, adapter }
48 }
49
50 pub fn with_adapter<CustomNode>(
51 self,
52 adapter: Arc<CustomNode>,
53 ) -> DecisionEngine<L, CustomNode>
54 where
55 CustomNode: CustomNodeAdapter,
56 {
57 DecisionEngine { loader: self.loader, adapter }
58 }
59
60 pub fn with_loader<Loader>(
61 self,
62 loader: Arc<Loader>,
63 ) -> DecisionEngine<Loader, A>
64 where
65 Loader: DecisionLoader,
66 {
67 DecisionEngine { loader, adapter: self.adapter }
68 }
69
70 pub fn with_closure_loader<F, O>(
71 self,
72 loader: F,
73 ) -> DecisionEngine<ClosureLoader<F>, A>
74 where
75 F: Fn(String) -> O + Sync + Send,
76 O: Future<Output = LoaderResponse> + Send,
77 {
78 DecisionEngine {
79 loader: Arc::new(ClosureLoader::new(loader)),
80 adapter: self.adapter,
81 }
82 }
83
84 pub async fn evaluate<K>(
86 &self,
87 key: K,
88 context: Variable,
89 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
90 where
91 K: AsRef<str>,
92 {
93 self.evaluate_with_opts(key, context, Default::default()).await
94 }
95 pub async fn evaluate_with_state_and_opts<K, S: Send + Sync + 'static>(
99 &self,
100 key: K,
101 context: Variable,
102 state: Arc<S>,
103 options: EvaluationOptions,
104 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
105 where
106 K: AsRef<str>,
107 {
108 let _guard = moduforge_rules_expression::StateGuard::new(state);
110
111 self.evaluate_with_opts(key, context, options).await
113 }
114 pub async fn evaluate_with_state<K, S: Send + Sync + 'static>(
118 &self,
119 key: K,
120 context: Variable,
121 state: Arc<S>,
122 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
123 where
124 K: AsRef<str>,
125 {
126 let _guard = moduforge_rules_expression::StateGuard::new(state);
128
129 self.evaluate(key, context).await
131 }
132
133 pub async fn evaluate_with_opts<K>(
135 &self,
136 key: K,
137 context: Variable,
138 options: EvaluationOptions,
139 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
140 where
141 K: AsRef<str>,
142 {
143 let content = self.loader.load(key.as_ref()).await?;
144 let decision = self.create_decision(content);
145 decision.evaluate_with_opts(context, options).await
146 }
147
148 pub fn create_decision(
150 &self,
151 content: Arc<DecisionContent>,
152 ) -> Decision<L, A> {
153 Decision::from(content)
154 .with_loader(self.loader.clone())
155 .with_adapter(self.adapter.clone())
156 }
157
158 pub async fn get_decision(
160 &self,
161 key: &str,
162 ) -> LoaderResult<Decision<L, A>> {
163 let content = self.loader.load(key).await?;
164 Ok(self.create_decision(content))
165 }
166
167 pub fn loader(&self) -> &L {
168 self.loader.as_ref()
169 }
170
171 pub fn adapter(&self) -> &A {
172 self.adapter.as_ref()
173 }
174}