1use crate::magic::{Function, FunctionRegistry, IntoFunction};
2use crate::objects::{TryIntoValue, Value};
3use crate::{functions, ExecutionError};
4use cel_parser::Expression;
5use std::collections::HashMap;
6
7pub enum Context<'a> {
33 Root {
34 functions: FunctionRegistry,
35 variables: HashMap<String, Value>,
36 },
37 Child {
38 parent: &'a Context<'a>,
39 variables: HashMap<String, Value>,
40 },
41}
42
43impl Context<'_> {
44 pub fn add_variable<S, V>(
45 &mut self,
46 name: S,
47 value: V,
48 ) -> Result<(), <V as TryIntoValue>::Error>
49 where
50 S: Into<String>,
51 V: TryIntoValue,
52 {
53 match self {
54 Context::Root { variables, .. } => {
55 variables.insert(name.into(), value.try_into_value()?);
56 }
57 Context::Child { variables, .. } => {
58 variables.insert(name.into(), value.try_into_value()?);
59 }
60 }
61 Ok(())
62 }
63
64 pub fn add_variable_from_value<S, V>(&mut self, name: S, value: V)
65 where
66 S: Into<String>,
67 V: Into<Value>,
68 {
69 match self {
70 Context::Root { variables, .. } => {
71 variables.insert(name.into(), value.into());
72 }
73 Context::Child { variables, .. } => {
74 variables.insert(name.into(), value.into());
75 }
76 }
77 }
78
79 pub fn get_variable<S>(&self, name: S) -> Result<Value, ExecutionError>
80 where
81 S: AsRef<str>,
82 {
83 let name = name.as_ref();
84 match self {
85 Context::Child { variables, parent } => variables
86 .get(name)
87 .cloned()
88 .or_else(|| parent.get_variable(name).ok())
89 .ok_or_else(|| ExecutionError::UndeclaredReference(name.to_string().into())),
90 Context::Root { variables, .. } => variables
91 .get(name)
92 .cloned()
93 .ok_or_else(|| ExecutionError::UndeclaredReference(name.to_string().into())),
94 }
95 }
96
97 pub(crate) fn has_function(&self, name: &str) -> bool {
98 match self {
99 Context::Root { functions, .. } => functions.has(name),
100 Context::Child { parent, .. } => parent.has_function(name),
101 }
102 }
103
104 pub(crate) fn get_function(&self, name: &str) -> Option<&Function> {
105 match self {
106 Context::Root { functions, .. } => functions.get(name),
107 Context::Child { parent, .. } => parent.get_function(name),
108 }
109 }
110
111 pub fn add_function<T: 'static, F>(&mut self, name: &str, value: F)
112 where
113 F: IntoFunction<T> + 'static + Send + Sync,
114 {
115 if let Context::Root { functions, .. } = self {
116 functions.add(name, value);
117 };
118 }
119
120 pub fn resolve(&self, expr: &Expression) -> Result<Value, ExecutionError> {
121 Value::resolve(expr, self)
122 }
123
124 pub fn resolve_all(&self, exprs: &[Expression]) -> Result<Value, ExecutionError> {
125 Value::resolve_all(exprs, self)
126 }
127
128 pub fn new_inner_scope(&self) -> Context {
129 Context::Child {
130 parent: self,
131 variables: Default::default(),
132 }
133 }
134
135 pub fn empty() -> Self {
147 Context::Root {
148 variables: Default::default(),
149 functions: Default::default(),
150 }
151 }
152}
153
154impl Default for Context<'_> {
155 fn default() -> Self {
156 let mut ctx = Context::Root {
157 variables: Default::default(),
158 functions: Default::default(),
159 };
160
161 ctx.add_function("contains", functions::contains);
162 ctx.add_function("size", functions::size);
163 ctx.add_function("max", functions::max);
164 ctx.add_function("min", functions::min);
165 ctx.add_function("startsWith", functions::starts_with);
166 ctx.add_function("endsWith", functions::ends_with);
167 ctx.add_function("string", functions::string);
168 ctx.add_function("bytes", functions::bytes);
169 ctx.add_function("double", functions::double);
170 ctx.add_function("int", functions::int);
171 ctx.add_function("uint", functions::uint);
172
173 #[cfg(feature = "regex")]
174 ctx.add_function("matches", functions::matches);
175
176 #[cfg(feature = "chrono")]
177 {
178 ctx.add_function("duration", functions::duration);
179 ctx.add_function("timestamp", functions::timestamp);
180 ctx.add_function("getFullYear", functions::time::timestamp_year);
181 ctx.add_function("getMonth", functions::time::timestamp_month);
182 ctx.add_function("getDayOfYear", functions::time::timestamp_year_day);
183 ctx.add_function("getDayOfMonth", functions::time::timestamp_month_day);
184 ctx.add_function("getDate", functions::time::timestamp_date);
185 ctx.add_function("getDayOfWeek", functions::time::timestamp_weekday);
186 ctx.add_function("getHours", functions::time::timestamp_hours);
187 ctx.add_function("getMinutes", functions::time::timestamp_minutes);
188 ctx.add_function("getSeconds", functions::time::timestamp_seconds);
189 ctx.add_function("getMilliseconds", functions::time::timestamp_millis);
190 }
191
192 ctx
193 }
194}