1use crate::bindings::attributes::AttributesBindingAdapter;
6use crate::context::attributes::AttributesContext;
7use crate::context::authentication::AuthenticationContext;
8use crate::context::input::InputContext;
9use crate::context::merged::MergedContext;
10use crate::context::payload::PayloadContext;
11use crate::context::reference::TransientContext;
12use crate::context::vars::VarsContext;
13use crate::context::StreamSolver;
14use crate::value::IntoValue;
15use crate::{AttributesBinding, AuthenticationBinding, Format, PayloadBinding, Value};
16use pel::expression::Expression as PelExpression;
17use pel::runtime::value::Value as PelValue;
18use pel::runtime::{Context, Evaluation, Runtime, RuntimeError};
19use std::cell::RefCell;
20use std::collections::HashMap;
21use std::convert::TryFrom;
22use thiserror::Error;
23
24#[cfg(feature = "stream_body")]
25use classy::hl::BodyStream;
26
27#[allow(unused)]
29use crate::Script;
30
31pub struct Evaluator<'a> {
33 stream_solver: StreamSolver,
34 evaluation: Option<PelExpression>,
35 result: Option<PelValue>,
36 error: Option<RuntimeError>,
37
38 input_context: &'a InputContext,
39 transient_context: TransientContext,
40 vars: HashMap<String, Value>,
41
42 content_type: RefCell<Option<Format>>,
43}
44
45impl<'a> Evaluator<'a> {
46 pub(crate) fn new(
47 stream_solver: StreamSolver,
48 evaluation: Option<PelExpression>,
49 result: Option<PelValue>,
50 context: &'a InputContext,
51 ) -> Self {
52 Self {
53 stream_solver,
54 evaluation,
55 result,
56 error: None,
57 input_context: context,
58 transient_context: TransientContext::default(),
59 vars: HashMap::new(),
60 content_type: RefCell::new(None),
61 }
62 }
63
64 pub fn bind_vars<K: IntoValue>(&mut self, name: &str, value: K) {
67 if !self.is_ready() {
68 self.vars.insert(name.to_string(), value.into_value());
69 if self
70 .input_context
71 .vars()
72 .iter()
73 .all(|item| self.vars.contains_key(item))
74 {
75 let vars = self.vars.drain().collect();
76 self.do_eval(&VarsContext::new(self.input_context, vars));
77 }
78 }
79 }
80
81 pub fn bind_payload(&mut self, binding: &dyn PayloadBinding) {
83 if !self.is_ready() {
84 let content_type = *self.content_type.borrow();
85 self.do_eval(&PayloadContext::new(
86 self.input_context,
87 binding,
88 content_type,
89 ))
90 }
91 }
92
93 #[cfg(feature = "stream_body")]
94 pub async fn bind_stream_body(&mut self, body: BodyStream<'_>) {
96 if !self.is_ready() {
97 let content_type = *self.content_type.borrow();
98
99 let body = self.stream_solver.bind_body_stream(body).await;
100
101 self.do_eval(&PayloadContext::new(
102 self.input_context,
103 &body,
104 content_type,
105 ))
106 }
107 }
108
109 pub fn bind_attributes(&mut self, binding: &dyn AttributesBinding) {
111 if !self.is_ready() {
112 Self::extract_format(binding)
113 .map(|content_type| self.content_type.borrow_mut().replace(content_type));
114
115 self.stream_solver.bind_attributes(binding);
116 let adapter = AttributesBindingAdapter::new(binding);
117 self.do_eval(&AttributesContext::new(self.input_context, &adapter))
118 }
119 }
120
121 pub fn bind_authentication(&mut self, binding: &dyn AuthenticationBinding) {
123 if !self.is_ready() {
124 self.do_eval(&AuthenticationContext::new(self.input_context, binding))
125 }
126 }
127
128 fn extract_format(binding: &dyn AttributesBinding) -> Option<Format> {
129 binding.extract_header("content-type").and_then(|header| {
130 if header.contains("/xml") {
131 Some(Format::Xml)
132 } else if header.contains("/json") {
133 Some(Format::Json)
134 } else if header.contains("text/plain") {
135 Some(Format::PlainText)
136 } else {
137 None
138 }
139 })
140 }
141
142 fn do_eval(&mut self, bound_context: &'_ dyn Context) {
143 let local_context = MergedContext::new(self.input_context, &self.transient_context);
144 let merged_context = MergedContext::new(&local_context, bound_context);
145 if let Some(expr) = self.evaluation.take() {
146 match Runtime::new().eval_with_context(&expr, &merged_context) {
147 Ok(eval) => match eval {
148 Evaluation::Complete(_, v) => {
149 self.result = Some(v);
150 }
151 Evaluation::Partial(exp) => {
152 self.transient_context.detach_pending(&exp, bound_context);
153 self.evaluation = Some(exp);
154 }
155 },
156 Err(error) => {
157 self.error = Some(error);
158 }
159 }
160 }
161 }
162
163 pub fn is_ready(&self) -> bool {
165 self.result.is_some() || self.error.is_some()
166 }
167
168 pub fn eval(self) -> Result<Value, EvaluationError> {
171 if let Some(error) = self.error {
172 return Err(EvaluationError::Error(error));
173 }
174
175 self.result
176 .ok_or(EvaluationError::PartialEvaluation)
177 .and_then(|val| Value::try_from(&val))
178 }
179}
180
181#[derive(Error, Debug)]
183pub enum EvaluationError {
184 #[error("Evaluation is incomplete")]
186 PartialEvaluation,
187
188 #[error("Unsupported DataType")]
190 TypeMismatch,
191
192 #[error("Error evaluating expression: {0}")]
194 Error(RuntimeError),
195}