dsntk_model_evaluator/
model_evaluator.rs

1//! # Decision model evaluator
2
3use crate::business_knowledge_model::BusinessKnowledgeModelEvaluator;
4use crate::decision::DecisionEvaluator;
5use crate::decision_service::DecisionServiceEvaluator;
6use crate::input_data::InputDataEvaluator;
7use crate::item_definition::ItemDefinitionEvaluator;
8use crate::model_builder::{EvaluatorBuilders, ModelBuilder};
9use crate::model_definitions::{DefKey, InvocableType, Invocables};
10use dsntk_common::Result;
11use dsntk_feel::context::FeelContext;
12use dsntk_feel::values::Value;
13use dsntk_feel::{value_null, Name};
14use dsntk_model::Definitions;
15use std::sync::Arc;
16
17/// Model evaluator.
18pub struct ModelEvaluator {
19  /// Input data evaluator.
20  input_data_evaluator: InputDataEvaluator,
21  /// Item definition evaluator.
22  item_definition_evaluator: ItemDefinitionEvaluator,
23  /// Business knowledge model evaluator.
24  business_knowledge_model_evaluator: BusinessKnowledgeModelEvaluator,
25  /// Decision evaluator.
26  decision_evaluator: DecisionEvaluator,
27  /// Decision service evaluator.
28  decision_service_evaluator: DecisionServiceEvaluator,
29  /// Map of invocables indexed by invocable name.
30  invocables: Invocables,
31  /// Map of global information item types defined in this model evaluator.
32  global_context: FeelContext,
33}
34
35impl From<ModelBuilder> for ModelEvaluator {
36  /// Creates [ModelEvaluator] from provided [ModelBuilder].
37  fn from(model_builder: ModelBuilder) -> Self {
38    let builders: EvaluatorBuilders = model_builder.into();
39    let mut global_context = FeelContext::default();
40    for (def_key, feel_type) in builders.information_item_types {
41      global_context.set_entry(&Name::from(def_key.id()), Value::FeelType(feel_type))
42    }
43    Self {
44      input_data_evaluator: builders.input_data_evaluator,
45      item_definition_evaluator: builders.item_definition_evaluator,
46      business_knowledge_model_evaluator: builders.business_knowledge_model_evaluator,
47      decision_evaluator: builders.decision_evaluator,
48      decision_service_evaluator: builders.decision_service_evaluator,
49      invocables: builders.invocables,
50      global_context,
51    }
52  }
53}
54
55impl ModelEvaluator {
56  /// Creates an instance of [ModelEvaluator] from parsed [Definitions].
57  pub fn new(definitions: &[Definitions]) -> Result<Arc<Self>> {
58    let mut model_builder = ModelBuilder::default();
59    definitions.iter().for_each(|definitions| model_builder.add_model(definitions));
60    model_builder.build()?;
61    let model_evaluator: Arc<ModelEvaluator> = Arc::new(model_builder.into());
62    model_evaluator.decision_service_evaluator.build_function_definitions(&Arc::clone(&model_evaluator));
63    Ok(model_evaluator)
64  }
65
66  /// Returns a reference to input data evaluator.
67  pub fn input_data_evaluator(&self) -> &InputDataEvaluator {
68    &self.input_data_evaluator
69  }
70
71  /// Returns a reference to item definition evaluator.
72  pub fn item_definition_evaluator(&self) -> &ItemDefinitionEvaluator {
73    &self.item_definition_evaluator
74  }
75
76  /// Returns a reference to business knowledge model evaluator.
77  pub fn business_knowledge_model_evaluator(&self) -> &BusinessKnowledgeModelEvaluator {
78    &self.business_knowledge_model_evaluator
79  }
80
81  /// Returns a reference to decision evaluator.
82  pub fn decision_evaluator(&self) -> &DecisionEvaluator {
83    &self.decision_evaluator
84  }
85
86  /// Returns a reference to decision service evaluator.
87  pub fn decision_service_evaluator(&self) -> &DecisionServiceEvaluator {
88    &self.decision_service_evaluator
89  }
90
91  /// Returns a reference to invocables in model evaluator.
92  pub fn invocables(&self) -> &Invocables {
93    &self.invocables
94  }
95
96  /// Evaluates an invocable.
97  pub fn evaluate_invocable(&self, model_namespace: &str, model_name: &str, invocable_name: &str, input_data: &FeelContext) -> Value {
98    let Some(invocable) = self.invocables.by_name(model_namespace, model_name, invocable_name) else {
99      return value_null!("invocable '{}' not found in namespace '{}'", invocable_name, model_namespace);
100    };
101    match invocable {
102      InvocableType::Decision(def_key) => {
103        // evaluate a decision
104        self.evaluate_decision(def_key, input_data)
105      }
106      InvocableType::BusinessKnowledgeModel(def_key, output_variable_name) => {
107        // evaluate a business knowledge model
108        self.evaluate_bkm(def_key, input_data, output_variable_name)
109      }
110      InvocableType::DecisionService(def_key) => {
111        // evaluate a decision service
112        self.evaluate_decision_service(def_key, input_data)
113      }
114    }
115  }
116
117  /// Evaluates a decision.
118  fn evaluate_decision(&self, def_key: &DefKey, input_data: &FeelContext) -> Value {
119    let mut evaluated_ctx = FeelContext::default();
120    if let Some(output_variable_name) = self.decision_evaluator.evaluate(def_key, &self.global_context, input_data, self, &mut evaluated_ctx) {
121      if let Some(output_value) = evaluated_ctx.get_entry(&output_variable_name) {
122        output_value.clone()
123      } else {
124        value_null!()
125      }
126    } else {
127      value_null!()
128    }
129  }
130
131  /// Evaluates a business knowledge model.
132  fn evaluate_bkm(&self, def_key: &DefKey, input_data: &FeelContext, output_variable_name: &Name) -> Value {
133    let mut evaluated_ctx = FeelContext::default();
134    self
135      .business_knowledge_model_evaluator
136      .evaluate(def_key, &self.global_context, input_data, self, &mut evaluated_ctx);
137    if let Some(Value::FunctionDefinition(parameters, body, _external, _, closure_ctx, result_type)) = evaluated_ctx.get_entry(output_variable_name) {
138      //TODO Handle external functions.
139      let mut parameters_ctx = FeelContext::default();
140      parameters_ctx.zip(closure_ctx);
141      for (name, _) in parameters {
142        if let Some(value) = input_data.get_entry(name) {
143          parameters_ctx.set_entry(name, value.to_owned());
144        }
145      }
146      parameters_ctx.zip(&evaluated_ctx);
147      let result = body.evaluate(&parameters_ctx.into());
148      result.coerced(result_type)
149    } else {
150      value_null!()
151    }
152  }
153
154  /// Evaluates a decision service.
155  fn evaluate_decision_service(&self, def_key: &DefKey, input_data: &FeelContext) -> Value {
156    let mut evaluated_ctx = FeelContext::default();
157    if let Some(output_variable_name) = self
158      .decision_service_evaluator
159      .evaluate(def_key, &self.global_context, input_data, self, &mut evaluated_ctx)
160    {
161      if let Some(output_value) = evaluated_ctx.get_entry(&output_variable_name) {
162        output_value.clone()
163      } else {
164        value_null!()
165      }
166    } else {
167      value_null!()
168    }
169  }
170}