1use crate::bindings::attributes::AttributesBindingAdapter;
4use crate::context::attributes::AttributesContext;
5use crate::context::authentication::AuthenticationContext;
6use crate::context::input::InputContext;
7use crate::context::merged::MergedContext;
8use crate::context::payload::PayloadContext;
9use crate::context::reference::TransientContext;
10use crate::context::vars::VarsContext;
11use crate::value::IntoValue;
12use crate::{AttributesBinding, AuthenticationBinding, PayloadBinding, Value};
13use pel::expression::Expression as PelExpression;
14use pel::runtime::value::Value as PelValue;
15use pel::runtime::{Context, Evaluation, Runtime, RuntimeError};
16use std::collections::HashMap;
17use std::convert::TryFrom;
18use thiserror::Error;
19
20#[allow(unused)]
22use crate::Script;
23
24pub struct Evaluator<'a> {
26 evaluation: Option<PelExpression>,
27 result: Option<PelValue>,
28 error: Option<RuntimeError>,
29
30 input_context: &'a InputContext,
31 transient_context: TransientContext,
32 vars: HashMap<String, Value>,
33}
34
35impl<'a> Evaluator<'a> {
36 pub(crate) fn new(
37 evaluation: Option<PelExpression>,
38 result: Option<PelValue>,
39 context: &'a InputContext,
40 ) -> Self {
41 Self {
42 evaluation,
43 result,
44 error: None,
45 input_context: context,
46 transient_context: TransientContext::default(),
47 vars: HashMap::new(),
48 }
49 }
50
51 pub fn bind_vars<K: IntoValue>(&mut self, name: &str, value: K) {
54 if !self.is_ready() {
55 self.vars.insert(name.to_string(), value.into_value());
56 if self
57 .input_context
58 .vars()
59 .iter()
60 .all(|item| self.vars.contains_key(item))
61 {
62 let vars = self.vars.drain().collect();
63 self.do_eval(&VarsContext::new(self.input_context, vars));
64 }
65 }
66 }
67
68 pub fn bind_payload(&mut self, binding: &dyn PayloadBinding) {
70 if !self.is_ready() {
71 self.do_eval(&PayloadContext::new(self.input_context, binding))
72 }
73 }
74
75 pub fn bind_attributes(&mut self, binding: &dyn AttributesBinding) {
77 if !self.is_ready() {
78 let adapter = AttributesBindingAdapter::new(binding);
79 self.do_eval(&AttributesContext::new(self.input_context, &adapter))
80 }
81 }
82
83 pub fn bind_authentication(&mut self, binding: &dyn AuthenticationBinding) {
85 if !self.is_ready() {
86 self.do_eval(&AuthenticationContext::new(self.input_context, binding))
87 }
88 }
89
90 fn do_eval(&mut self, bound_context: &'_ dyn Context) {
91 let local_context = MergedContext::new(self.input_context, &self.transient_context);
92 let merged_context = MergedContext::new(&local_context, bound_context);
93 if let Some(expr) = self.evaluation.take() {
94 match Runtime::new().eval_with_context(&expr, &merged_context) {
95 Ok(eval) => match eval {
96 Evaluation::Complete(_, v) => {
97 self.result = Some(v);
98 }
99 Evaluation::Partial(exp) => {
100 self.transient_context.detach_pending(&exp, bound_context);
101 self.evaluation = Some(exp);
102 }
103 },
104 Err(error) => {
105 self.error = Some(error);
106 }
107 }
108 }
109 }
110
111 pub fn is_ready(&self) -> bool {
113 self.result.is_some() || self.error.is_some()
114 }
115
116 pub fn eval(self) -> Result<Value, EvaluationError> {
119 if let Some(error) = self.error {
120 return Err(EvaluationError::Error(error));
121 }
122
123 self.result
124 .ok_or(EvaluationError::PartialEvaluation)
125 .and_then(|val| Value::try_from(&val))
126 }
127}
128
129#[derive(Error, Debug)]
131pub enum EvaluationError {
132 #[error("Evaluation is incomplete")]
134 PartialEvaluation,
135
136 #[error("Unsupported DataType")]
138 TypeMismatch,
139
140 #[error("Error evaluating expression: {0}")]
142 Error(RuntimeError),
143}