mini_builder_rs/evaluator/
evaluation_context.rs1use std::collections::HashMap;
2
3use crate::value::Value;
4
5use super::{Evaluate, ValueFunction, Variables};
6
7#[derive(Clone)]
8pub enum ContextLocation {
10 SourceOrTemplate(String, bool),
12 Variables,
14}
15
16impl ContextLocation {
17 pub fn partial(name: impl ToString, is_source: bool) -> Self {
18 Self::SourceOrTemplate(name.to_string(), is_source)
19 }
20
21 pub fn source(name: impl ToString) -> Self {
22 Self::partial(name, true)
23 }
24
25 pub fn template(name: impl ToString) -> Self {
26 Self::partial(name, false)
27 }
28}
29
30#[derive(Clone, Copy)]
32pub struct EvaluationContextWarnings {
33 pub max_depth_reached: bool,
34 pub variable_not_found: bool,
35 pub function_not_found: bool,
36 pub evaluator_not_found: bool,
37}
38
39impl std::default::Default for EvaluationContextWarnings {
40 fn default() -> Self {
41 Self {
42 max_depth_reached: true,
43 variable_not_found: true,
44 function_not_found: true,
45 evaluator_not_found: true,
46 }
47 }
48}
49
50pub struct EvaluationContext<'c, E: Evaluate> {
52 global_variables: &'c Variables,
54 local_variables: Vec<Variables>,
55 evaluators: &'c HashMap<String, E>,
57 functions: &'c HashMap<String, ValueFunction>,
58 depth: usize,
60 max_depth: usize,
61 warnings: EvaluationContextWarnings,
63 location_stack: Vec<ContextLocation>,
64}
65
66impl<'c, E: Evaluate> EvaluationContext<'c, E> {
67 pub fn new(
68 global_variables: &'c Variables,
69 mut local_variables: Vec<Variables>,
70 builders: &'c HashMap<String, E>,
71 functions: &'c HashMap<String, Box<dyn Fn(&[Value]) -> Value + 'static>>,
72 max_depth: usize,
73 warnings: EvaluationContextWarnings,
74 context_location: ContextLocation,
75 ) -> Self {
76 if local_variables.is_empty() {
77 local_variables.push(HashMap::new());
78 }
79
80 Self {
81 global_variables,
82 local_variables,
83 evaluators: builders,
84 functions,
85 depth: 0,
86 max_depth,
87 warnings,
88 location_stack: vec![context_location],
89 }
90 }
91
92 pub fn spawn_new(
93 &self,
94 local_variables: Vec<Variables>,
95 context_location: ContextLocation,
96 ) -> Result<Self, ()> {
97 let depth = self.depth + 1;
98 if depth == self.max_depth {
99 if self.warnings.max_depth_reached {
100 let trace = self.trace();
101 let tail_len = 5.min(trace.len());
102 let trace = &trace[trace.len() - tail_len..];
103 log::warn!("Max depth reached: trace tail: {:?}", trace)
104 }
105 return Err(());
106 }
107
108 let mut location_stack = self.location_stack.clone();
109 location_stack.push(context_location);
110
111 Ok(Self {
112 global_variables: self.global_variables,
113 local_variables,
114 evaluators: self.evaluators,
115 functions: self.functions,
116 depth,
117 max_depth: self.max_depth,
118 warnings: self.warnings.clone(),
119 location_stack,
120 })
121 }
122
123 fn trace(&self) -> Vec<String> {
124 self.location_stack
125 .iter()
126 .map(|location| match location {
127 ContextLocation::SourceOrTemplate(name, is_source) => {
128 if *is_source {
129 format!("source '{name}'")
130 } else {
131 format!("template '{name}'")
132 }
133 }
134 ContextLocation::Variables => format!("variables"),
135 })
136 .collect()
137 }
138
139 pub fn get_evaluator(&self, name: &str) -> Option<&'c E> {
140 let ret = self.evaluators.get(name);
141
142 if self.warnings.evaluator_not_found {
144 if ret.is_none() {
145 log::warn!(
146 "No evaluator named `{name}` was found. Trace: {:?}.",
147 self.trace()
148 );
149 }
150 }
151
152 ret
153 }
154
155 pub fn get_function(&self, name: &str) -> Option<&'c ValueFunction> {
156 let ret = self.functions.get(name);
157
158 if self.warnings.function_not_found {
160 if ret.is_none() {
161 log::warn!(
162 "No function named `{name}` was found. Trace: {:?}.",
163 self.trace()
164 );
165 }
166 }
167
168 ret
169 }
170
171 pub fn get_variable_value(&self, name: &str) -> Option<Value> {
172 for variables in self.local_variables.iter().rev() {
174 let v = variables.get(name);
175 if v.is_some() {
176 return v.cloned();
177 }
178 }
179
180 let v = self.global_variables.get(name);
182 if v.is_some() {
183 return v.cloned();
184 }
185
186 if self.warnings.variable_not_found {
188 log::warn!(
189 "No variable named `{name}` was found. Trace: {:?}.",
190 self.trace()
191 );
192 }
193
194 None
195 }
196
197 pub fn push_variables(&mut self, variables: Variables) {
198 self.local_variables.push(variables);
199 }
200
201 pub fn pop_variables(&mut self) {
202 self.local_variables.pop();
203 }
204
205 pub fn assign_local_variable(&mut self, name: &str, value: Value) {
206 self.local_variables
207 .last_mut()
208 .unwrap()
209 .insert(name.to_string(), value);
210 }
211}