1extern crate core;
32
33use std::{convert::TryFrom, sync::Arc};
34
35use thiserror::Error;
36
37mod macros;
38
39pub mod common;
40pub mod context;
41pub mod parser;
42
43pub use common::ast::IdedExpr;
44use common::ast::SelectExpr;
45pub use context::Context;
46pub use functions::FunctionContext;
47pub use objects::{ResolveResult, Value};
48use parser::{Expression, ExpressionReferences, Parser};
49pub use parser::{ParseError, ParseErrors};
50pub mod functions;
51mod magic;
52pub mod objects;
53mod resolvers;
54
55#[cfg(feature = "time")]
56pub mod duration;
57
58#[cfg(feature = "ip")]
59pub use ser::Ip;
60#[cfg(feature = "time")]
61pub use ser::{Duration, Timestamp};
62
63mod ser;
64pub use ser::{SerializationError, to_value};
65
66#[cfg(feature = "json")]
67mod json;
68#[cfg(feature = "json")]
69pub use json::ConvertToJsonError;
70use magic::FromContext;
71
72pub mod extractors {
73 pub use crate::magic::{Arguments, Identifier, This};
74}
75
76#[derive(Error, Clone, Debug, PartialEq)]
77#[non_exhaustive]
78pub enum ExecutionError {
79 #[error("Invalid argument count: expected {expected}, got {actual}")]
80 InvalidArgumentCount { expected: usize, actual: usize },
81 #[error("Invalid argument type: {:?}", .target)]
82 UnsupportedTargetType { target: Value },
83 #[error("Method '{method}' not supported on type '{target:?}'")]
84 NotSupportedAsMethod { method: String, target: Value },
85 #[error("Unable to use value '{0:?}' as a key")]
88 UnsupportedKeyType(Value),
89 #[error("Unexpected type: got '{got}', want '{want}'")]
90 UnexpectedType { got: String, want: String },
91 #[error("No such key: {0}")]
94 NoSuchKey(Arc<String>),
95 #[error("No such overload")]
98 NoSuchOverload,
99 #[error("Undeclared reference to '{0}'")]
102 UndeclaredReference(Arc<String>),
103 #[error("Missing argument or target")]
106 MissingArgumentOrTarget,
107 #[error("{0:?} can not be compared to {1:?}")]
109 ValuesNotComparable(Value, Value),
110 #[error("Unsupported unary operator '{0}': {1:?}")]
112 UnsupportedUnaryOperator(&'static str, Value),
113 #[error("Unsupported binary operator '{0}': {1:?}, {2:?}")]
116 UnsupportedBinaryOperator(&'static str, Value, Value),
117 #[error("Cannot use value as map index: {0:?}")]
119 UnsupportedMapIndex(Value),
120 #[error("Cannot use value as list index: {0:?}")]
122 UnsupportedListIndex(Value),
123 #[error("Cannot use value {0:?} to index {1:?}")]
125 UnsupportedIndex(Value, Value),
126 #[error("Unsupported function call identifier type: {0:?}")]
129 UnsupportedFunctionCallIdentifierType(Expression),
130 #[error("Unsupported fields construction: {0:?}")]
133 UnsupportedFieldsConstruction(SelectExpr),
134 #[error("Error executing function '{function}': {message}")]
136 FunctionError { function: String, message: String },
137 #[error("Division by zero of {0:?}")]
138 DivisionByZero(Value),
139 #[error("Remainder by zero of {0:?}")]
140 RemainderByZero(Value),
141 #[error("Overflow from binary operator '{0}': {1:?}, {2:?}")]
142 Overflow(&'static str, Value, Value),
143}
144
145impl ExecutionError {
146 pub fn no_such_key(name: &str) -> Self {
147 ExecutionError::NoSuchKey(Arc::new(name.to_string()))
148 }
149
150 pub fn undeclared_reference(name: &str) -> Self {
151 ExecutionError::UndeclaredReference(Arc::new(name.to_string()))
152 }
153
154 pub fn invalid_argument_count(expected: usize, actual: usize) -> Self {
155 ExecutionError::InvalidArgumentCount {
156 expected,
157 actual,
158 }
159 }
160
161 pub fn function_error<E: ToString>(function: &str, error: E) -> Self {
162 ExecutionError::FunctionError {
163 function: function.to_string(),
164 message: error.to_string(),
165 }
166 }
167
168 pub fn unsupported_target_type(target: Value) -> Self {
169 ExecutionError::UnsupportedTargetType {
170 target,
171 }
172 }
173
174 pub fn not_supported_as_method(method: &str, target: Value) -> Self {
175 ExecutionError::NotSupportedAsMethod {
176 method: method.to_string(),
177 target,
178 }
179 }
180
181 pub fn unsupported_key_type(value: Value) -> Self {
182 ExecutionError::UnsupportedKeyType(value)
183 }
184
185 pub fn missing_argument_or_target() -> Self {
186 ExecutionError::MissingArgumentOrTarget
187 }
188}
189
190#[derive(Debug, Clone)]
191pub struct Program {
192 expression: Expression,
193}
194
195impl Program {
196 pub fn compile(source: &str) -> Result<Program, ParseErrors> {
197 let parser = Parser::default();
198 parser.parse(source).map(|expression| Program {
199 expression,
200 })
201 }
202
203 pub fn execute(&self, context: &Context) -> ResolveResult {
204 Value::resolve(&self.expression, context)
205 }
206
207 pub fn references(&self) -> ExpressionReferences<'_> {
219 self.expression.references()
220 }
221
222 pub fn expression(&self) -> &Expression {
224 &self.expression
225 }
226}
227
228impl TryFrom<&str> for Program {
229 type Error = ParseErrors;
230
231 fn try_from(value: &str) -> Result<Self, Self::Error> {
232 Program::compile(value)
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use std::{collections::HashMap, convert::TryInto};
239
240 use crate::{
241 ExecutionError, Program,
242 context::Context,
243 objects::{ResolveResult, Value},
244 };
245
246 pub(crate) fn test_script(script: &str, ctx: Option<Context>) -> ResolveResult {
248 let program = match Program::compile(script) {
249 Ok(p) => p,
250 Err(e) => panic!("{}", e),
251 };
252 program.execute(&ctx.unwrap_or_default())
253 }
254
255 #[test]
256 fn parse() {
257 Program::compile("1 + 1").unwrap();
258 }
259
260 #[test]
261 fn from_str() {
262 let input = "1.1";
263 let _p: Program = input.try_into().unwrap();
264 }
265
266 #[test]
267 fn variables() {
268 fn assert_output(script: &str, expected: ResolveResult) {
269 let mut ctx = Context::default();
270 ctx.add_variable_from_value("foo", HashMap::from([("bar", 1i64)]));
271 ctx.add_variable_from_value("arr", vec![1i64, 2, 3]);
272 ctx.add_variable_from_value("str", "foobar".to_string());
273 assert_eq!(test_script(script, Some(ctx)), expected);
274 }
275
276 assert_output("length([1, 2, 3]) == 3", Ok(true.into()));
278 assert_output("length([length([42]), 2, 3]) == 3", Ok(true.into()));
279 assert_output("length([]) == 3", Ok(false.into()));
280
281 assert_output("foo.bar == 1", Ok(true.into()));
283
284 assert_output("arr[0] == 1", Ok(true.into()));
286
287 assert_output("str[0] == \"f\"", Ok(true.into()));
289 }
290
291 #[test]
292 fn references() {
293 let p = Program::compile("[1, 1].map(x, x * 2)").unwrap();
294 assert!(p.references().has_variable("x"));
295 assert_eq!(p.references().variables().len(), 1);
296 }
297
298 #[test]
299 fn test_execution_errors() {
300 let tests = vec![
301 ("no such key", "foo.baz.bar == 1", ExecutionError::no_such_key("baz")),
302 (
303 "undeclared reference",
304 "missing == 1",
305 ExecutionError::undeclared_reference("missing"),
306 ),
307 (
308 "undeclared method",
309 "1.missing()",
310 ExecutionError::undeclared_reference("missing"),
311 ),
312 (
313 "undeclared function",
314 "missing(1)",
315 ExecutionError::undeclared_reference("missing"),
316 ),
317 (
318 "unsupported key type",
319 "{null: true}",
320 ExecutionError::unsupported_key_type(Value::Null),
321 ),
322 ];
323
324 for (name, script, error) in tests {
325 let mut ctx = Context::default();
326 ctx.add_variable_from_value("foo", HashMap::from([("bar", 1)]));
327 let res = test_script(script, Some(ctx));
328 assert_eq!(res, error.into(), "{name}");
329 }
330 }
331}