1extern crate core;
2
3use std::convert::TryFrom;
4use std::sync::Arc;
5use thiserror::Error;
6
7mod macros;
8
9pub mod context;
10pub use cel_parser::ast::IdedExpr;
11use cel_parser::ast::SelectExpr;
12use cel_parser::{Expression, ExpressionReferences, Parser};
13pub use cel_parser::{ParseError, ParseErrors};
14pub use context::Context;
15pub use functions::FunctionContext;
16pub use objects::{ResolveResult, Value};
17pub mod functions;
18mod magic;
19pub mod objects;
20mod resolvers;
21
22#[cfg(feature = "chrono")]
23mod duration;
24#[cfg(feature = "chrono")]
25pub use ser::{Duration, Timestamp};
26
27mod ser;
28pub use ser::to_value;
29pub use ser::SerializationError;
30
31#[cfg(feature = "json")]
32mod json;
33#[cfg(feature = "json")]
34pub use json::ConvertToJsonError;
35
36use magic::FromContext;
37
38pub mod extractors {
39 pub use crate::magic::{Arguments, Identifier, This};
40}
41
42#[derive(Error, Clone, Debug, PartialEq)]
43#[non_exhaustive]
44pub enum ExecutionError {
45 #[error("Invalid argument count: expected {expected}, got {actual}")]
46 InvalidArgumentCount { expected: usize, actual: usize },
47 #[error("Invalid argument type: {:?}", .target)]
48 UnsupportedTargetType { target: Value },
49 #[error("Method '{method}' not supported on type '{target:?}'")]
50 NotSupportedAsMethod { method: String, target: Value },
51 #[error("Unable to use value '{0:?}' as a key")]
54 UnsupportedKeyType(Value),
55 #[error("Unexpected type: got '{got}', want '{want}'")]
56 UnexpectedType { got: String, want: String },
57 #[error("No such key: {0}")]
60 NoSuchKey(Arc<String>),
61 #[error("Undeclared reference to '{0}'")]
64 UndeclaredReference(Arc<String>),
65 #[error("Missing argument or target")]
68 MissingArgumentOrTarget,
69 #[error("{0:?} can not be compared to {1:?}")]
71 ValuesNotComparable(Value, Value),
72 #[error("Unsupported unary operator '{0}': {1:?}")]
74 UnsupportedUnaryOperator(&'static str, Value),
75 #[error("Unsupported binary operator '{0}': {1:?}, {2:?}")]
78 UnsupportedBinaryOperator(&'static str, Value, Value),
79 #[error("Cannot use value as map index: {0:?}")]
81 UnsupportedMapIndex(Value),
82 #[error("Cannot use value as list index: {0:?}")]
84 UnsupportedListIndex(Value),
85 #[error("Cannot use value {0:?} to index {1:?}")]
87 UnsupportedIndex(Value, Value),
88 #[error("Unsupported function call identifier type: {0:?}")]
91 UnsupportedFunctionCallIdentifierType(Expression),
92 #[error("Unsupported fields construction: {0:?}")]
95 UnsupportedFieldsConstruction(SelectExpr),
96 #[error("Error executing function '{function}': {message}")]
98 FunctionError { function: String, message: String },
99 #[error("Division by zero of {0:?}")]
100 DivisionByZero(Value),
101 #[error("Remainder by zero of {0:?}")]
102 RemainderByZero(Value),
103 #[error("Overflow from binary operator '{0}': {1:?}, {2:?}")]
104 Overflow(&'static str, Value, Value),
105}
106
107impl ExecutionError {
108 pub fn no_such_key(name: &str) -> Self {
109 ExecutionError::NoSuchKey(Arc::new(name.to_string()))
110 }
111
112 pub fn undeclared_reference(name: &str) -> Self {
113 ExecutionError::UndeclaredReference(Arc::new(name.to_string()))
114 }
115
116 pub fn invalid_argument_count(expected: usize, actual: usize) -> Self {
117 ExecutionError::InvalidArgumentCount { expected, actual }
118 }
119
120 pub fn function_error<E: ToString>(function: &str, error: E) -> Self {
121 ExecutionError::FunctionError {
122 function: function.to_string(),
123 message: error.to_string(),
124 }
125 }
126
127 pub fn unsupported_target_type(target: Value) -> Self {
128 ExecutionError::UnsupportedTargetType { target }
129 }
130
131 pub fn not_supported_as_method(method: &str, target: Value) -> Self {
132 ExecutionError::NotSupportedAsMethod {
133 method: method.to_string(),
134 target,
135 }
136 }
137
138 pub fn unsupported_key_type(value: Value) -> Self {
139 ExecutionError::UnsupportedKeyType(value)
140 }
141
142 pub fn missing_argument_or_target() -> Self {
143 ExecutionError::MissingArgumentOrTarget
144 }
145}
146
147#[derive(Debug)]
148pub struct Program {
149 expression: Expression,
150}
151
152impl Program {
153 pub fn compile(source: &str) -> Result<Program, ParseErrors> {
154 let parser = Parser::default();
155 parser
156 .parse(source)
157 .map(|expression| Program { expression })
158 }
159
160 pub fn execute(&self, context: &Context) -> ResolveResult {
161 Value::resolve(&self.expression, context)
162 }
163
164 pub fn references(&self) -> ExpressionReferences {
176 self.expression.references()
177 }
178}
179
180impl TryFrom<&str> for Program {
181 type Error = ParseErrors;
182
183 fn try_from(value: &str) -> Result<Self, Self::Error> {
184 Program::compile(value)
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use crate::context::Context;
191 use crate::objects::{ResolveResult, Value};
192 use crate::{ExecutionError, Program};
193 use std::collections::HashMap;
194 use std::convert::TryInto;
195
196 pub(crate) fn test_script(script: &str, ctx: Option<Context>) -> ResolveResult {
198 let program = match Program::compile(script) {
199 Ok(p) => p,
200 Err(e) => panic!("{}", e),
201 };
202 program.execute(&ctx.unwrap_or_default())
203 }
204
205 #[test]
206 fn parse() {
207 Program::compile("1 + 1").unwrap();
208 }
209
210 #[test]
211 fn from_str() {
212 let input = "1.1";
213 let _p: Program = input.try_into().unwrap();
214 }
215
216 #[test]
217 fn variables() {
218 fn assert_output(script: &str, expected: ResolveResult) {
219 let mut ctx = Context::default();
220 ctx.add_variable_from_value("foo", HashMap::from([("bar", 1i64)]));
221 ctx.add_variable_from_value("arr", vec![1i64, 2, 3]);
222 ctx.add_variable_from_value("str", "foobar".to_string());
223 assert_eq!(test_script(script, Some(ctx)), expected);
224 }
225
226 assert_output("size([1, 2, 3]) == 3", Ok(true.into()));
228 assert_output("size([]) == 3", Ok(false.into()));
229
230 assert_output("foo.bar == 1", Ok(true.into()));
232
233 assert_output("arr[0] == 1", Ok(true.into()));
235
236 assert_output("str[0] == 'f'", Ok(true.into()));
238 }
239
240 #[test]
241 fn references() {
242 let p = Program::compile("[1, 1].map(x, x * 2)").unwrap();
243 assert!(p.references().has_variable("x"));
244 assert_eq!(p.references().variables().len(), 1);
245 }
246
247 #[test]
248 fn test_execution_errors() {
249 let tests = vec![
250 (
251 "no such key",
252 "foo.baz.bar == 1",
253 ExecutionError::no_such_key("baz"),
254 ),
255 (
256 "undeclared reference",
257 "missing == 1",
258 ExecutionError::undeclared_reference("missing"),
259 ),
260 (
261 "undeclared method",
262 "1.missing()",
263 ExecutionError::undeclared_reference("missing"),
264 ),
265 (
266 "undeclared function",
267 "missing(1)",
268 ExecutionError::undeclared_reference("missing"),
269 ),
270 (
271 "unsupported key type",
272 "{null: true}",
273 ExecutionError::unsupported_key_type(Value::Null),
274 ),
275 ];
276
277 for (name, script, error) in tests {
278 let mut ctx = Context::default();
279 ctx.add_variable_from_value("foo", HashMap::from([("bar", 1)]));
280 let res = test_script(script, Some(ctx));
281 assert_eq!(res, error.into(), "{name}");
282 }
283 }
284}