zen_engine/
decision.rs

1use crate::engine::EvaluationOptions;
2use crate::handler::custom_node_adapter::{CustomNodeAdapter, NoopCustomNode};
3use crate::handler::graph::{DecisionGraph, DecisionGraphConfig, DecisionGraphResponse};
4use crate::loader::{CachedLoader, DecisionLoader, NoopLoader};
5use crate::model::DecisionContent;
6use crate::util::validator_cache::ValidatorCache;
7use crate::{DecisionGraphValidationError, EvaluationError};
8use std::sync::Arc;
9use zen_expression::variable::Variable;
10
11/// Represents a JDM decision which can be evaluated
12#[derive(Debug, Clone)]
13pub struct Decision<Loader, CustomNode>
14where
15    Loader: DecisionLoader + 'static,
16    CustomNode: CustomNodeAdapter + 'static,
17{
18    content: Arc<DecisionContent>,
19    loader: Arc<Loader>,
20    adapter: Arc<CustomNode>,
21
22    validator_cache: ValidatorCache,
23}
24
25impl From<DecisionContent> for Decision<NoopLoader, NoopCustomNode> {
26    fn from(value: DecisionContent) -> Self {
27        Self {
28            content: value.into(),
29            loader: NoopLoader::default().into(),
30            adapter: NoopCustomNode::default().into(),
31
32            validator_cache: Default::default(),
33        }
34    }
35}
36
37impl From<Arc<DecisionContent>> for Decision<NoopLoader, NoopCustomNode> {
38    fn from(value: Arc<DecisionContent>) -> Self {
39        Self {
40            content: value,
41            loader: NoopLoader::default().into(),
42            adapter: NoopCustomNode::default().into(),
43
44            validator_cache: Default::default(),
45        }
46    }
47}
48
49impl<L, A> Decision<L, A>
50where
51    L: DecisionLoader + 'static,
52    A: CustomNodeAdapter + 'static,
53{
54    pub fn with_loader<Loader>(self, loader: Arc<Loader>) -> Decision<Loader, A>
55    where
56        Loader: DecisionLoader,
57    {
58        Decision {
59            loader,
60            adapter: self.adapter,
61            content: self.content,
62            validator_cache: self.validator_cache,
63        }
64    }
65
66    pub fn with_adapter<Adapter>(self, adapter: Arc<Adapter>) -> Decision<L, Adapter>
67    where
68        Adapter: CustomNodeAdapter,
69    {
70        Decision {
71            loader: self.loader,
72            adapter,
73            content: self.content,
74            validator_cache: self.validator_cache,
75        }
76    }
77
78    /// Evaluates a decision using an in-memory reference stored in struct
79    pub async fn evaluate(
80        &self,
81        context: Variable,
82    ) -> Result<DecisionGraphResponse, Box<EvaluationError>> {
83        self.evaluate_with_opts(context, Default::default()).await
84    }
85
86    /// Evaluates a decision using in-memory reference with advanced options
87    pub async fn evaluate_with_opts(
88        &self,
89        context: Variable,
90        options: EvaluationOptions,
91    ) -> Result<DecisionGraphResponse, Box<EvaluationError>> {
92        let mut decision_graph = DecisionGraph::try_new(DecisionGraphConfig {
93            content: self.content.clone(),
94            max_depth: options.max_depth.unwrap_or(5),
95            trace: options.trace.unwrap_or_default(),
96            loader: Arc::new(CachedLoader::from(self.loader.clone())),
97            adapter: self.adapter.clone(),
98            iteration: 0,
99            validator_cache: Some(self.validator_cache.clone()),
100        })?;
101
102        let response = decision_graph.evaluate(context).await?;
103
104        Ok(response)
105    }
106
107    pub fn validate(&self) -> Result<(), DecisionGraphValidationError> {
108        let decision_graph = DecisionGraph::try_new(DecisionGraphConfig {
109            content: self.content.clone(),
110            max_depth: 1,
111            trace: false,
112            loader: Arc::new(CachedLoader::from(self.loader.clone())),
113            adapter: self.adapter.clone(),
114            iteration: 0,
115            validator_cache: Some(self.validator_cache.clone()),
116        })?;
117
118        decision_graph.validate()
119    }
120}