Skip to main content

jpx_core/
runtime.rs

1//! JMESPath runtime for compiling and evaluating expressions.
2
3use std::collections::HashMap;
4
5use crate::Expression;
6use crate::JmespathError;
7use crate::functions::*;
8use crate::parse;
9use crate::registry::{Category, FunctionRegistry};
10
11/// Compiles JMESPath expressions and manages registered functions.
12pub 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    /// Creates a new Runtime with no functions registered.
26    pub fn new() -> Runtime {
27        Default::default()
28    }
29
30    /// Creates a strict-mode runtime with only the 26 spec functions.
31    pub fn strict() -> Runtime {
32        let mut rt = Runtime::new();
33        rt.register_builtin_functions();
34        rt
35    }
36
37    /// Creates a RuntimeBuilder for selective function registration.
38    pub fn builder() -> RuntimeBuilder {
39        RuntimeBuilder::new()
40    }
41
42    /// Creates a new JMESPath expression from an expression string.
43    #[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    /// Adds a new function to the runtime.
49    #[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    /// Removes a function from the runtime.
55    pub fn deregister_function(&mut self, name: &str) -> Option<Box<dyn Function>> {
56        self.functions.remove(name)
57    }
58
59    /// Gets a function by name from the runtime.
60    #[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    /// Returns an iterator over all registered function names.
66    pub fn function_names(&self) -> impl Iterator<Item = &str> {
67        self.functions.keys().map(|s| s.as_str())
68    }
69
70    /// Registers all 26 built-in JMESPath functions.
71    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
101/// Builder for creating a Runtime with selective function registration.
102///
103/// # Example
104///
105/// ```
106/// use jpx_core::Runtime;
107/// use jpx_core::Category;
108///
109/// let rt = Runtime::builder()
110///     .with_standard()
111///     .with_category(Category::String)
112///     .with_category(Category::Math)
113///     .build();
114/// ```
115pub 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    /// Includes the 26 standard JMESPath functions.
129    pub fn with_standard(mut self) -> Self {
130        self.include_standard = true;
131        self
132    }
133
134    /// Includes all functions from a specific category.
135    pub fn with_category(mut self, category: Category) -> Self {
136        self.registry.register_category(category);
137        self
138    }
139
140    /// Includes all available extension functions.
141    pub fn with_all_extensions(mut self) -> Self {
142        self.registry.register_all();
143        self
144    }
145
146    /// Disables a specific function.
147    pub fn without_function(mut self, name: &str) -> Self {
148        self.registry.disable_function(name);
149        self
150    }
151
152    /// Builds the runtime with the configured functions.
153    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}