1use std::collections::HashMap;
2
3use crate::{
4 ExecutionError, functions,
5 magic::{Function, FunctionRegistry, IntoFunction},
6 objects::{TryIntoValue, Value},
7 parser::Expression,
8};
9
10pub enum Context<'a> {
36 Root {
37 functions: FunctionRegistry,
38 variables: HashMap<String, Value>,
39 },
40 Child {
41 parent: &'a Context<'a>,
42 variables: HashMap<String, Value>,
43 },
44}
45
46impl Context<'_> {
47 pub fn add_variable<S, V>(&mut self, name: S, value: V) -> Result<(), <V as TryIntoValue>::Error>
48 where
49 S: Into<String>,
50 V: TryIntoValue,
51 {
52 match self {
53 Context::Root {
54 variables, ..
55 } => {
56 variables.insert(name.into(), value.try_into_value()?);
57 }
58 Context::Child {
59 variables, ..
60 } => {
61 variables.insert(name.into(), value.try_into_value()?);
62 }
63 }
64 Ok(())
65 }
66
67 pub fn add_variable_from_value<S, V>(&mut self, name: S, value: V)
68 where
69 S: Into<String>,
70 V: Into<Value>,
71 {
72 match self {
73 Context::Root {
74 variables, ..
75 } => {
76 variables.insert(name.into(), value.into());
77 }
78 Context::Child {
79 variables, ..
80 } => {
81 variables.insert(name.into(), value.into());
82 }
83 }
84 }
85
86 pub fn get_variable<S>(&self, name: S) -> Result<Value, ExecutionError>
87 where
88 S: AsRef<str>,
89 {
90 let name = name.as_ref();
91 match self {
92 Context::Child {
93 variables,
94 parent,
95 } => variables
96 .get(name)
97 .cloned()
98 .or_else(|| parent.get_variable(name).ok())
99 .ok_or_else(|| ExecutionError::UndeclaredReference(name.to_string().into())),
100 Context::Root {
101 variables, ..
102 } => variables
103 .get(name)
104 .cloned()
105 .ok_or_else(|| ExecutionError::UndeclaredReference(name.to_string().into())),
106 }
107 }
108
109 pub(crate) fn get_function(&self, name: &str) -> Option<&Function> {
110 match self {
111 Context::Root {
112 functions, ..
113 } => functions.get(name),
114 Context::Child {
115 parent, ..
116 } => parent.get_function(name),
117 }
118 }
119
120 pub fn add_function<T: 'static, F>(&mut self, name: &str, value: F)
121 where
122 F: IntoFunction<T> + 'static + Send + Sync,
123 {
124 if let Context::Root {
125 functions, ..
126 } = self
127 {
128 functions.add(name, value);
129 };
130 }
131
132 pub fn resolve(&self, expr: &Expression) -> Result<Value, ExecutionError> {
133 Value::resolve(expr, self)
134 }
135
136 pub fn resolve_all(&self, exprs: &[Expression]) -> Result<Value, ExecutionError> {
137 Value::resolve_all(exprs, self)
138 }
139
140 pub fn new_inner_scope(&self) -> Context<'_> {
141 Context::Child {
142 parent: self,
143 variables: Default::default(),
144 }
145 }
146
147 pub fn empty() -> Self {
159 Context::Root {
160 variables: Default::default(),
161 functions: Default::default(),
162 }
163 }
164}
165
166impl Default for Context<'_> {
167 fn default() -> Self {
168 let mut ctx = Context::Root {
169 variables: Default::default(),
170 functions: Default::default(),
171 };
172
173 ctx.add_function("contains", functions::contains);
174 ctx.add_function("length", functions::length);
175 ctx.add_function("max", functions::max);
176 ctx.add_function("min", functions::min);
177 ctx.add_function("starts_with", functions::starts_with);
178 ctx.add_function("ends_with", functions::ends_with);
179
180 ctx.add_function("String", functions::string);
181 ctx.add_function("Bytes", functions::bytes);
182 ctx.add_function("Float", functions::float);
183 ctx.add_function("Int", functions::int);
184 #[cfg(feature = "regex")]
187 {
188 ctx.add_function("matches", functions::matches);
189 ctx.add_function("Regex", functions::regex);
190 }
191
192 #[cfg(feature = "time")]
193 {
194 ctx.add_function("Duration", functions::duration);
195 ctx.add_function("Timestamp", functions::timestamp);
196
197 ctx.add_function("year", functions::time::timestamp_year);
198 ctx.add_function("month", functions::time::timestamp_month);
199 ctx.add_function("seconds", functions::time::timestamp_seconds);
200 ctx.add_function("milliseconds", functions::time::timestamp_millis);
201 ctx.add_function("unix", functions::time::unix);
202 ctx.add_function("now", functions::time::now);
203
204 ctx.add_function("getDayOfYear", functions::time::timestamp_year_day);
205 ctx.add_function("getDayOfMonth", functions::time::timestamp_month_day);
206 ctx.add_function("getDate", functions::time::timestamp_date);
207 ctx.add_function("getDayOfWeek", functions::time::timestamp_weekday);
208 ctx.add_function("getHours", functions::time::timestamp_hours);
209 ctx.add_function("getMinutes", functions::time::timestamp_minutes);
210 }
211
212 #[cfg(feature = "ip")]
213 {
214 ctx.add_function("Ip", functions::ip);
215 }
216
217 ctx
218 }
219}