1use 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::{ClosureLoader, DecisionLoader, LoaderResponse, LoaderResult, NoopLoader};
8use crate::model::DecisionContent;
9use crate::EvaluationError;
10use zen_expression::variable::Variable;
11
12#[derive(Debug, Clone)]
14pub struct DecisionEngine<Loader, CustomNode>
15where
16 Loader: DecisionLoader + 'static,
17 CustomNode: CustomNodeAdapter + 'static,
18{
19 loader: Arc<Loader>,
20 adapter: Arc<CustomNode>,
21}
22
23#[derive(Debug, Default)]
24pub struct EvaluationOptions {
25 pub trace: Option<bool>,
26 pub max_depth: Option<u8>,
27}
28
29impl Default for DecisionEngine<NoopLoader, NoopCustomNode> {
30 fn default() -> Self {
31 Self {
32 loader: Arc::new(NoopLoader::default()),
33 adapter: Arc::new(NoopCustomNode::default()),
34 }
35 }
36}
37
38impl<L: DecisionLoader + 'static, A: CustomNodeAdapter + 'static> DecisionEngine<L, A> {
39 pub fn new(loader: Arc<L>, adapter: Arc<A>) -> Self {
40 Self { loader, adapter }
41 }
42
43 pub fn with_adapter<CustomNode>(self, adapter: Arc<CustomNode>) -> DecisionEngine<L, CustomNode>
44 where
45 CustomNode: CustomNodeAdapter,
46 {
47 DecisionEngine {
48 loader: self.loader,
49 adapter,
50 }
51 }
52
53 pub fn with_loader<Loader>(self, loader: Arc<Loader>) -> DecisionEngine<Loader, A>
54 where
55 Loader: DecisionLoader,
56 {
57 DecisionEngine {
58 loader,
59 adapter: self.adapter,
60 }
61 }
62
63 pub fn with_closure_loader<F, O>(self, loader: F) -> DecisionEngine<ClosureLoader<F>, A>
64 where
65 F: Fn(String) -> O + Sync + Send,
66 O: Future<Output = LoaderResponse> + Send,
67 {
68 DecisionEngine {
69 loader: Arc::new(ClosureLoader::new(loader)),
70 adapter: self.adapter,
71 }
72 }
73
74 pub async fn evaluate<K>(
76 &self,
77 key: K,
78 context: Variable,
79 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
80 where
81 K: AsRef<str>,
82 {
83 self.evaluate_with_opts(key, context, Default::default())
84 .await
85 }
86
87 pub async fn evaluate_with_opts<K>(
89 &self,
90 key: K,
91 context: Variable,
92 options: EvaluationOptions,
93 ) -> Result<DecisionGraphResponse, Box<EvaluationError>>
94 where
95 K: AsRef<str>,
96 {
97 let content = self.loader.load(key.as_ref()).await?;
98 let decision = self.create_decision(content);
99 decision.evaluate_with_opts(context, options).await
100 }
101
102 pub fn create_decision(&self, content: Arc<DecisionContent>) -> Decision<L, A> {
104 Decision::from(content)
105 .with_loader(self.loader.clone())
106 .with_adapter(self.adapter.clone())
107 }
108
109 pub async fn get_decision(&self, key: &str) -> LoaderResult<Decision<L, A>> {
111 let content = self.loader.load(key).await?;
112 Ok(self.create_decision(content))
113 }
114
115 pub fn loader(&self) -> &L {
116 self.loader.as_ref()
117 }
118
119 pub fn adapter(&self) -> &A {
120 self.adapter.as_ref()
121 }
122}