dmntk_model_evaluator/
business_knowledge_model.rs

1//! # Builder for business knowledge model evaluators
2
3use crate::boxed_expressions::*;
4use crate::errors::*;
5use crate::model_builder::ModelBuilder;
6use crate::model_definitions::{DefBusinessKnowledgeModel, DefDefinitions, DefKey};
7use crate::model_evaluator::ModelEvaluator;
8use dmntk_common::Result;
9use dmntk_feel::closure::Closure;
10use dmntk_feel::context::FeelContext;
11use dmntk_feel::values::Value;
12use dmntk_feel::{FeelScope, FeelType, FunctionBody, Name};
13use dmntk_model::*;
14use std::collections::HashMap;
15use std::sync::Arc;
16
17/// Type of closure that evaluates business knowledge model.
18///
19/// (global_context, input data, model evaluator, output data)
20///
21type BusinessKnowledgeModelEvaluatorFn = Box<dyn Fn(&FeelContext, &FeelContext, &ModelEvaluator, &mut FeelContext) -> Name + Send + Sync>;
22
23/// Business knowledge model evaluator.
24#[derive(Default)]
25pub struct BusinessKnowledgeModelEvaluator {
26  evaluators: Arc<HashMap<DefKey, BusinessKnowledgeModelEvaluatorFn>>,
27}
28
29impl BusinessKnowledgeModelEvaluator {
30  /// Creates a new business knowledge model evaluator.
31  pub fn new(definitions: &DefDefinitions, model_builder: &ModelBuilder) -> Result<Self> {
32    let mut evaluators = HashMap::new();
33    for business_knowledge_model in definitions.business_knowledge_models() {
34      let function_definition = business_knowledge_model.encapsulated_logic().as_ref().ok_or_else(err_empty_encapsulated_logic)?;
35      let evaluator = build_bkm_evaluator(definitions, business_knowledge_model, function_definition, model_builder)?;
36      let namespace = business_knowledge_model.namespace();
37      let id = business_knowledge_model.id();
38      let name = business_knowledge_model.name().to_string();
39      let output_variable_name = business_knowledge_model.variable().name().to_owned();
40      let def_key = DefKey::new(namespace, id);
41      evaluators.insert(def_key.clone(), evaluator);
42      model_builder.add_bkm_invocable(namespace.to_string(), name, def_key, output_variable_name);
43    }
44    Ok(Self { evaluators: Arc::new(evaluators) })
45  }
46
47  /// Evaluates a business knowledge model with specified identifier.
48  /// When a required business knowledge model is found, then its evaluator
49  /// is executed, and the result is stored in `evaluated_ctx`.
50  pub fn evaluate(
51    &self,
52    def_key: &DefKey,
53    global_context: &FeelContext,
54    input_data: &FeelContext,
55    model_evaluator: &ModelEvaluator,
56    output_data: &mut FeelContext,
57  ) -> Option<Name> {
58    self
59      .evaluators
60      .get(def_key)
61      .map(|evaluator_entry| evaluator_entry(global_context, input_data, model_evaluator, output_data))
62  }
63}
64
65fn build_bkm_evaluator(
66  definitions: &DefDefinitions,
67  business_knowledge_model: &DefBusinessKnowledgeModel,
68  function_definition: &FunctionDefinition,
69  model_builder: &ModelBuilder,
70) -> Result<BusinessKnowledgeModelEvaluatorFn> {
71  let item_definition_type_evaluator = model_builder.item_definition_type_evaluator();
72  let mut local_context = FeelContext::default();
73  let mut formal_parameters = vec![];
74  for information_item in function_definition.formal_parameters() {
75    let feel_type = item_definition_type_evaluator
76      .information_item_type(information_item.namespace(), information_item.type_ref())
77      .ok_or_else(err_empty_feel_type)?;
78    let feel_name = information_item.feel_name();
79    formal_parameters.push((feel_name.clone(), feel_type.clone()));
80    local_context.set_entry(feel_name, Value::FeelType(feel_type));
81  }
82  let output_variable_name = business_knowledge_model.variable().name().clone();
83  let output_variable_type = item_definition_type_evaluator
84    .information_item_type(business_knowledge_model.variable().namespace(), business_knowledge_model.variable().type_ref())
85    .unwrap_or(FeelType::Any);
86  let mut knowledge_requirements: Vec<DefKey> = vec![];
87  for knowledge_requirement in business_knowledge_model.knowledge_requirements() {
88    knowledge_requirements.push(knowledge_requirement.required_knowledge().into());
89  }
90  // bring into context the variables from knowledge requirements
91  bring_knowledge_requirements_into_context(definitions, business_knowledge_model.knowledge_requirements(), &mut local_context)?;
92  //TODO verify the above line - there was no such example in models
93  if let Some(expression_instance) = function_definition.body() {
94    let scope: FeelScope = local_context.into();
95    build_bkm_expression_instance_evaluator(
96      &scope,
97      formal_parameters,
98      expression_instance,
99      output_variable_name,
100      output_variable_type,
101      knowledge_requirements,
102      model_builder,
103    )
104  } else {
105    let output_variable_name = business_knowledge_model.variable().name().clone();
106    Ok(Box::new(move |_: &FeelContext, _: &FeelContext, _: &ModelEvaluator, _: &mut FeelContext| {
107      output_variable_name.clone()
108    }))
109  }
110}
111
112fn build_bkm_expression_instance_evaluator(
113  scope: &FeelScope,
114  formal_parameters: Vec<(Name, FeelType)>,
115  expression_instance: &ExpressionInstance,
116  output_variable_name: Name,
117  output_variable_type: FeelType,
118  knowledge_requirements: Vec<DefKey>,
119  model_builder: &ModelBuilder,
120) -> Result<BusinessKnowledgeModelEvaluatorFn> {
121  match expression_instance {
122    ExpressionInstance::Context(context) => {
123      //
124      build_bkm_context_evaluator(
125        scope,                  //
126        formal_parameters,      //
127        context,                //
128        output_variable_name,   //
129        output_variable_type,   //
130        knowledge_requirements, //
131        model_builder,          //
132      )
133    }
134    ExpressionInstance::DecisionTable(decision_table) => {
135      //
136      build_bkm_decision_table_evaluator(
137        scope,                  //
138        formal_parameters,      //
139        decision_table,         //
140        output_variable_name,   //
141        output_variable_type,   //
142        knowledge_requirements, //
143        model_builder,          //
144      )
145    }
146    ExpressionInstance::FunctionDefinition(function_definition) => {
147      //
148      build_bkm_function_definition_evaluator(
149        scope,                  //
150        formal_parameters,      //
151        function_definition,    //
152        output_variable_name,   //
153        output_variable_type,   //
154        knowledge_requirements, //
155        model_builder,          //
156      )
157    }
158    ExpressionInstance::Invocation(invocation) => {
159      //
160      build_bkm_invocation_evaluator(
161        scope,                  //
162        formal_parameters,      //
163        invocation,             //
164        output_variable_name,   //
165        output_variable_type,   //
166        knowledge_requirements, //
167        model_builder,          //
168      )
169    }
170    ExpressionInstance::LiteralExpression(literal_expression) => {
171      //
172      build_bkm_literal_expression_evaluator(
173        scope,                  //
174        formal_parameters,      //
175        literal_expression,     //
176        output_variable_name,   //
177        output_variable_type,   //
178        knowledge_requirements, //
179        model_builder,          //
180      )
181    }
182    ExpressionInstance::List(list) => {
183      //
184      build_bkm_list_evaluator(
185        scope,                  //
186        formal_parameters,      //
187        list,                   //
188        output_variable_name,   //
189        output_variable_type,   //
190        knowledge_requirements, //
191        model_builder,          //
192      )
193    }
194    ExpressionInstance::Relation(relation) => {
195      //
196      build_bkm_relation_evaluator(
197        scope,                  //
198        formal_parameters,      //
199        relation,               //
200        output_variable_name,   //
201        output_variable_type,   //
202        knowledge_requirements, //
203        model_builder,          //
204      )
205    }
206  }
207}
208
209fn build_bkm_context_evaluator(
210  scope: &FeelScope,
211  formal_parameters: Vec<(Name, FeelType)>,
212  context: &Context,
213  output_variable_name: Name,
214  output_variable_type: FeelType,
215  knowledge_requirements: Vec<DefKey>,
216  model_builder: &ModelBuilder,
217) -> Result<BusinessKnowledgeModelEvaluatorFn> {
218  let (evaluator, _) = build_context_evaluator(scope, context, model_builder)?;
219  let closure = Closure::default();
220  let closure_ctx = FeelContext::default();
221  let function = Value::FunctionDefinition(
222    formal_parameters,
223    FunctionBody::Context(Arc::new(evaluator)),
224    false,
225    closure,
226    closure_ctx,
227    output_variable_type,
228  );
229  build_bkm_evaluator_from_function_definition(output_variable_name, function, knowledge_requirements)
230}
231
232fn build_bkm_decision_table_evaluator(
233  scope: &FeelScope,
234  formal_parameters: Vec<(Name, FeelType)>,
235  decision_table: &DecisionTable,
236  output_variable_name: Name,
237  output_variable_type: FeelType,
238  knowledge_requirements: Vec<DefKey>,
239  model_builder: &ModelBuilder,
240) -> Result<BusinessKnowledgeModelEvaluatorFn> {
241  let (evaluator, _) = build_decision_table_evaluator(scope, decision_table, model_builder)?;
242  let closure = Closure::default();
243  let closure_ctx = FeelContext::default();
244  let function = Value::FunctionDefinition(
245    formal_parameters,
246    FunctionBody::DecisionTable(Arc::new(evaluator)),
247    false,
248    closure,
249    closure_ctx,
250    output_variable_type,
251  );
252  build_bkm_evaluator_from_function_definition(output_variable_name, function, knowledge_requirements)
253}
254
255fn build_bkm_function_definition_evaluator(
256  scope: &FeelScope,
257  formal_parameters: Vec<(Name, FeelType)>,
258  function_definition: &FunctionDefinition,
259  output_variable_name: Name,
260  output_variable_type: FeelType,
261  knowledge_requirements: Vec<DefKey>,
262  model_builder: &ModelBuilder,
263) -> Result<BusinessKnowledgeModelEvaluatorFn> {
264  let (evaluator, _) = build_function_definition_evaluator(scope, function_definition, model_builder)?;
265  let closure = Closure::default();
266  let closure_ctx = FeelContext::default();
267  let function = Value::FunctionDefinition(
268    formal_parameters,
269    FunctionBody::FunctionDefinition(Arc::new(evaluator)),
270    false,
271    closure,
272    closure_ctx,
273    output_variable_type,
274  );
275  build_bkm_evaluator_from_function_definition(output_variable_name, function, knowledge_requirements)
276}
277
278fn build_bkm_invocation_evaluator(
279  scope: &FeelScope,
280  formal_parameters: Vec<(Name, FeelType)>,
281  invocation: &Invocation,
282  output_variable_name: Name,
283  output_variable_type: FeelType,
284  knowledge_requirements: Vec<DefKey>,
285  model_builder: &ModelBuilder,
286) -> Result<BusinessKnowledgeModelEvaluatorFn> {
287  let (evaluator, _) = build_invocation_evaluator(scope, invocation, model_builder)?;
288  let closure = Closure::default();
289  let closure_ctx = FeelContext::default();
290  let function = Value::FunctionDefinition(
291    formal_parameters,
292    FunctionBody::Invocation(Arc::new(evaluator)),
293    false,
294    closure,
295    closure_ctx,
296    output_variable_type,
297  );
298  build_bkm_evaluator_from_function_definition(output_variable_name, function, knowledge_requirements)
299}
300
301fn build_bkm_list_evaluator(
302  scope: &FeelScope,
303  formal_parameters: Vec<(Name, FeelType)>,
304  list: &List,
305  output_variable_name: Name,
306  output_variable_type: FeelType,
307  knowledge_requirements: Vec<DefKey>,
308  model_builder: &ModelBuilder,
309) -> Result<BusinessKnowledgeModelEvaluatorFn> {
310  let (evaluator, _) = build_list_evaluator(scope, list, model_builder)?;
311  let closure = Closure::default();
312  let closure_ctx = FeelContext::default();
313  let function = Value::FunctionDefinition(
314    formal_parameters,
315    FunctionBody::LiteralExpression(Arc::new(evaluator)),
316    false,
317    closure,
318    closure_ctx,
319    output_variable_type,
320  );
321  build_bkm_evaluator_from_function_definition(output_variable_name, function, knowledge_requirements)
322}
323
324fn build_bkm_literal_expression_evaluator(
325  scope: &FeelScope,
326  formal_parameters: Vec<(Name, FeelType)>,
327  literal_expression: &LiteralExpression,
328  output_variable_name: Name,
329  output_variable_type: FeelType,
330  knowledge_requirements: Vec<DefKey>,
331  model_builder: &ModelBuilder,
332) -> Result<BusinessKnowledgeModelEvaluatorFn> {
333  let (evaluator, _) = build_literal_expression_evaluator(scope, literal_expression, model_builder)?;
334  let closure = Closure::default();
335  let closure_ctx = FeelContext::default();
336  let function = Value::FunctionDefinition(
337    formal_parameters,
338    FunctionBody::LiteralExpression(Arc::new(evaluator)),
339    false,
340    closure,
341    closure_ctx,
342    output_variable_type,
343  );
344  build_bkm_evaluator_from_function_definition(output_variable_name, function, knowledge_requirements)
345}
346
347fn build_bkm_relation_evaluator(
348  scope: &FeelScope,
349  formal_parameters: Vec<(Name, FeelType)>,
350  relation: &Relation,
351  output_variable_name: Name,
352  output_variable_type: FeelType,
353  knowledge_requirements: Vec<DefKey>,
354  model_builder: &ModelBuilder,
355) -> Result<BusinessKnowledgeModelEvaluatorFn> {
356  let (evaluator, _) = build_relation_evaluator(scope, relation, model_builder)?;
357  let closure = Closure::default();
358  let closure_ctx = FeelContext::default();
359  let function_definition = Value::FunctionDefinition(
360    formal_parameters,
361    FunctionBody::Relation(Arc::new(evaluator)),
362    false,
363    closure,
364    closure_ctx,
365    output_variable_type,
366  );
367  build_bkm_evaluator_from_function_definition(output_variable_name, function_definition, knowledge_requirements)
368}
369
370fn build_bkm_evaluator_from_function_definition(
371  output_variable_name: Name,
372  function_definition: Value,
373  knowledge_requirements: Vec<DefKey>,
374) -> Result<BusinessKnowledgeModelEvaluatorFn> {
375  Ok(Box::new(
376    move |global_context: &FeelContext, input_data: &FeelContext, model_evaluator: &ModelEvaluator, output_data: &mut FeelContext| {
377      let business_knowledge_model_evaluator = model_evaluator.business_knowledge_model_evaluator();
378      let decision_service_evaluator = model_evaluator.decision_service_evaluator();
379      knowledge_requirements.iter().for_each(|def_key| {
380        //TODO refactor:
381        //  call either business knowledge model or decision service,
382        //  but not both!
383        business_knowledge_model_evaluator.evaluate(def_key, global_context, input_data, model_evaluator, output_data);
384        decision_service_evaluator.evaluate(def_key, global_context, input_data, model_evaluator, output_data);
385      });
386      output_data.set_entry(&output_variable_name, function_definition.clone());
387      output_variable_name.clone()
388    },
389  ))
390}