1use std::collections::HashMap;
4
5use crate::Expression;
6use crate::JmespathError;
7use crate::functions::*;
8use crate::parse;
9use crate::registry::{Category, FunctionRegistry};
10
11pub struct Runtime {
13 functions: HashMap<String, Box<dyn Function>>,
14}
15
16impl Default for Runtime {
17 fn default() -> Self {
18 Runtime {
19 functions: HashMap::with_capacity(26),
20 }
21 }
22}
23
24impl Runtime {
25 pub fn new() -> Runtime {
27 Default::default()
28 }
29
30 pub fn strict() -> Runtime {
32 let mut rt = Runtime::new();
33 rt.register_builtin_functions();
34 rt
35 }
36
37 pub fn builder() -> RuntimeBuilder {
39 RuntimeBuilder::new()
40 }
41
42 #[inline]
44 pub fn compile<'a>(&'a self, expression: &str) -> Result<Expression<'a>, JmespathError> {
45 parse(expression).map(|ast| Expression::new(expression, ast, self))
46 }
47
48 #[inline]
50 pub fn register_function(&mut self, name: &str, f: Box<dyn Function>) {
51 self.functions.insert(name.to_owned(), f);
52 }
53
54 pub fn deregister_function(&mut self, name: &str) -> Option<Box<dyn Function>> {
56 self.functions.remove(name)
57 }
58
59 #[inline]
61 pub fn get_function<'a>(&'a self, name: &str) -> Option<&'a dyn Function> {
62 self.functions.get(name).map(AsRef::as_ref)
63 }
64
65 pub fn function_names(&self) -> impl Iterator<Item = &str> {
67 self.functions.keys().map(|s| s.as_str())
68 }
69
70 pub fn register_builtin_functions(&mut self) {
72 self.register_function("abs", Box::new(AbsFn::new()));
73 self.register_function("avg", Box::new(AvgFn::new()));
74 self.register_function("ceil", Box::new(CeilFn::new()));
75 self.register_function("contains", Box::new(ContainsFn::new()));
76 self.register_function("ends_with", Box::new(EndsWithFn::new()));
77 self.register_function("floor", Box::new(FloorFn::new()));
78 self.register_function("join", Box::new(JoinFn::new()));
79 self.register_function("keys", Box::new(KeysFn::new()));
80 self.register_function("length", Box::new(LengthFn::new()));
81 self.register_function("map", Box::new(MapFn::new()));
82 self.register_function("min", Box::new(MinFn::new()));
83 self.register_function("max", Box::new(MaxFn::new()));
84 self.register_function("max_by", Box::new(MaxByFn::new()));
85 self.register_function("min_by", Box::new(MinByFn::new()));
86 self.register_function("merge", Box::new(MergeFn::new()));
87 self.register_function("not_null", Box::new(NotNullFn::new()));
88 self.register_function("reverse", Box::new(ReverseFn::new()));
89 self.register_function("sort", Box::new(SortFn::new()));
90 self.register_function("sort_by", Box::new(SortByFn::new()));
91 self.register_function("starts_with", Box::new(StartsWithFn::new()));
92 self.register_function("sum", Box::new(SumFn::new()));
93 self.register_function("to_array", Box::new(ToArrayFn::new()));
94 self.register_function("to_number", Box::new(ToNumberFn::new()));
95 self.register_function("to_string", Box::new(ToStringFn::new()));
96 self.register_function("type", Box::new(TypeFn::new()));
97 self.register_function("values", Box::new(ValuesFn::new()));
98 }
99}
100
101pub struct RuntimeBuilder {
116 registry: FunctionRegistry,
117 include_standard: bool,
118}
119
120impl RuntimeBuilder {
121 fn new() -> Self {
122 RuntimeBuilder {
123 registry: FunctionRegistry::new(),
124 include_standard: false,
125 }
126 }
127
128 pub fn with_standard(mut self) -> Self {
130 self.include_standard = true;
131 self
132 }
133
134 pub fn with_category(mut self, category: Category) -> Self {
136 self.registry.register_category(category);
137 self
138 }
139
140 pub fn with_all_extensions(mut self) -> Self {
142 self.registry.register_all();
143 self
144 }
145
146 pub fn without_function(mut self, name: &str) -> Self {
148 self.registry.disable_function(name);
149 self
150 }
151
152 pub fn build(self) -> Runtime {
154 let mut rt = Runtime::new();
155 if self.include_standard {
156 rt.register_builtin_functions();
157 }
158 self.registry.apply(&mut rt);
159 rt
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use serde_json::json;
167
168 #[test]
169 fn new_runtime_has_no_functions() {
170 let rt = Runtime::new();
171 assert!(rt.get_function("abs").is_none());
172 assert_eq!(rt.function_names().count(), 0);
173 }
174
175 #[test]
176 fn strict_runtime_has_26_builtins() {
177 let rt = Runtime::strict();
178 assert_eq!(rt.function_names().count(), 26);
179 assert!(rt.get_function("abs").is_some());
180 assert!(rt.get_function("length").is_some());
181 assert!(rt.get_function("sort").is_some());
182 }
183
184 #[test]
185 fn register_and_get_function() {
186 let mut rt = Runtime::new();
187 rt.register_function("abs", Box::new(AbsFn::new()));
188 assert!(rt.get_function("abs").is_some());
189 assert!(rt.get_function("nonexistent").is_none());
190 }
191
192 #[test]
193 fn deregister_function() {
194 let mut rt = Runtime::strict();
195 assert!(rt.get_function("abs").is_some());
196 let removed = rt.deregister_function("abs");
197 assert!(removed.is_some());
198 assert!(rt.get_function("abs").is_none());
199 assert_eq!(rt.function_names().count(), 25);
200 }
201
202 #[test]
203 fn deregister_nonexistent_returns_none() {
204 let mut rt = Runtime::new();
205 assert!(rt.deregister_function("nope").is_none());
206 }
207
208 #[test]
209 fn builder_with_standard() {
210 let rt = Runtime::builder().with_standard().build();
211 assert_eq!(rt.function_names().count(), 26);
212 }
213
214 #[test]
215 #[cfg(feature = "extensions")]
216 fn builder_with_category() {
217 let rt = Runtime::builder()
218 .with_standard()
219 .with_category(Category::String)
220 .build();
221 assert!(rt.function_names().count() > 26);
222 assert!(rt.get_function("lower").is_some());
223 }
224
225 #[test]
226 #[cfg(feature = "extensions")]
227 fn builder_with_all_extensions() {
228 let rt = Runtime::builder()
229 .with_standard()
230 .with_all_extensions()
231 .build();
232 assert!(rt.function_names().count() > 26);
233 }
234
235 #[test]
236 #[cfg(feature = "extensions")]
237 fn builder_without_function() {
238 let rt = Runtime::builder()
239 .with_standard()
240 .with_all_extensions()
241 .without_function("lower")
242 .build();
243 assert!(rt.get_function("lower").is_none());
244 assert!(rt.get_function("upper").is_some());
245 }
246
247 #[test]
248 fn compile_with_runtime() {
249 let rt = Runtime::strict();
250 let expr = rt.compile("length(@)").unwrap();
251 let result = expr.search(&json!([1, 2, 3])).unwrap();
252 assert_eq!(result, json!(3));
253 }
254
255 #[test]
256 fn unknown_function_compile_succeeds_search_fails() {
257 let rt = Runtime::new();
258 let expr = rt.compile("nonexistent(@)").unwrap();
259 let result = expr.search(&json!(null));
260 assert!(result.is_err());
261 }
262
263 #[test]
264 fn function_names_iterator() {
265 let rt = Runtime::strict();
266 let names: Vec<&str> = rt.function_names().collect();
267 assert!(names.contains(&"abs"));
268 assert!(names.contains(&"length"));
269 assert!(names.contains(&"values"));
270 }
271}