use crate::business_knowledge_model::BusinessKnowledgeModelEvaluator;
use crate::decision::DecisionEvaluator;
use crate::decision_service::DecisionServiceEvaluator;
use crate::input_data::InputDataEvaluator;
use crate::item_definition::ItemDefinitionEvaluator;
use crate::model_builder::{EvaluatorBuilders, ModelBuilder};
use crate::model_definitions::{DefKey, InvocableType, Invocables};
use dsntk_common::Result;
use dsntk_feel::context::FeelContext;
use dsntk_feel::values::Value;
use dsntk_feel::{value_null, Name};
use dsntk_model::Definitions;
use std::sync::Arc;
pub struct ModelEvaluator {
input_data_evaluator: InputDataEvaluator,
item_definition_evaluator: ItemDefinitionEvaluator,
business_knowledge_model_evaluator: BusinessKnowledgeModelEvaluator,
decision_evaluator: DecisionEvaluator,
decision_service_evaluator: DecisionServiceEvaluator,
invocables: Invocables,
global_context: FeelContext,
}
impl From<ModelBuilder> for ModelEvaluator {
fn from(model_builder: ModelBuilder) -> Self {
let builders: EvaluatorBuilders = model_builder.into();
let mut global_context = FeelContext::default();
for (def_key, feel_type) in builders.information_item_types {
global_context.set_entry(&Name::from(def_key.id()), Value::FeelType(feel_type))
}
Self {
input_data_evaluator: builders.input_data_evaluator,
item_definition_evaluator: builders.item_definition_evaluator,
business_knowledge_model_evaluator: builders.business_knowledge_model_evaluator,
decision_evaluator: builders.decision_evaluator,
decision_service_evaluator: builders.decision_service_evaluator,
invocables: builders.invocables,
global_context,
}
}
}
impl ModelEvaluator {
pub fn new(definitions: &[Definitions]) -> Result<Arc<Self>> {
let mut model_builder = ModelBuilder::default();
definitions.iter().for_each(|definitions| model_builder.add_model(definitions));
model_builder.build()?;
let model_evaluator: Arc<ModelEvaluator> = Arc::new(model_builder.into());
model_evaluator.decision_service_evaluator.build_function_definitions(&Arc::clone(&model_evaluator));
Ok(model_evaluator)
}
pub fn input_data_evaluator(&self) -> &InputDataEvaluator {
&self.input_data_evaluator
}
pub fn item_definition_evaluator(&self) -> &ItemDefinitionEvaluator {
&self.item_definition_evaluator
}
pub fn business_knowledge_model_evaluator(&self) -> &BusinessKnowledgeModelEvaluator {
&self.business_knowledge_model_evaluator
}
pub fn decision_evaluator(&self) -> &DecisionEvaluator {
&self.decision_evaluator
}
pub fn decision_service_evaluator(&self) -> &DecisionServiceEvaluator {
&self.decision_service_evaluator
}
pub fn invocables(&self) -> &Invocables {
&self.invocables
}
pub fn evaluate_invocable(&self, model_namespace: &str, model_name: &str, invocable_name: &str, input_data: &FeelContext) -> Value {
let Some(invocable) = self.invocables.by_name(model_namespace, model_name, invocable_name) else {
return value_null!("invocable '{}' not found in namespace '{}'", invocable_name, model_namespace);
};
match invocable {
InvocableType::Decision(def_key) => {
self.evaluate_decision(def_key, input_data)
}
InvocableType::BusinessKnowledgeModel(def_key, output_variable_name) => {
self.evaluate_bkm(def_key, input_data, output_variable_name)
}
InvocableType::DecisionService(def_key) => {
self.evaluate_decision_service(def_key, input_data)
}
}
}
fn evaluate_decision(&self, def_key: &DefKey, input_data: &FeelContext) -> Value {
let mut evaluated_ctx = FeelContext::default();
if let Some(output_variable_name) = self.decision_evaluator.evaluate(def_key, &self.global_context, input_data, self, &mut evaluated_ctx) {
if let Some(output_value) = evaluated_ctx.get_entry(&output_variable_name) {
output_value.clone()
} else {
value_null!()
}
} else {
value_null!()
}
}
fn evaluate_bkm(&self, def_key: &DefKey, input_data: &FeelContext, output_variable_name: &Name) -> Value {
let mut evaluated_ctx = FeelContext::default();
self
.business_knowledge_model_evaluator
.evaluate(def_key, &self.global_context, input_data, self, &mut evaluated_ctx);
if let Some(Value::FunctionDefinition(parameters, body, _external, _, closure_ctx, result_type)) = evaluated_ctx.get_entry(output_variable_name) {
let mut parameters_ctx = FeelContext::default();
parameters_ctx.zip(closure_ctx);
for (name, _) in parameters {
if let Some(value) = input_data.get_entry(name) {
parameters_ctx.set_entry(name, value.to_owned());
}
}
parameters_ctx.zip(&evaluated_ctx);
let result = body.evaluate(¶meters_ctx.into());
result.coerced(result_type)
} else {
value_null!()
}
}
fn evaluate_decision_service(&self, def_key: &DefKey, input_data: &FeelContext) -> Value {
let mut evaluated_ctx = FeelContext::default();
if let Some(output_variable_name) = self
.decision_service_evaluator
.evaluate(def_key, &self.global_context, input_data, self, &mut evaluated_ctx)
{
if let Some(output_value) = evaluated_ctx.get_entry(&output_variable_name) {
output_value.clone()
} else {
value_null!()
}
} else {
value_null!()
}
}
}