1extern crate core;
2
3use cel_parser::{parse, ExpressionReferences, Member};
4use std::convert::TryFrom;
5use std::sync::Arc;
6use thiserror::Error;
7
8mod macros;
9
10pub mod context;
11pub use cel_parser::error::ParseError;
12pub use cel_parser::Expression;
13pub use context::Context;
14pub use functions::FunctionContext;
15pub use objects::{ResolveResult, Value};
16pub mod functions;
17mod magic;
18pub mod objects;
19mod resolvers;
20
21#[cfg(feature = "chrono")]
22mod duration;
23#[cfg(feature = "chrono")]
24pub use ser::{Duration, Timestamp};
25
26mod ser;
27pub use ser::to_value;
28pub use ser::SerializationError;
29
30#[cfg(feature = "json")]
31mod json;
32#[cfg(feature = "json")]
33pub use json::ConvertToJsonError;
34
35use magic::FromContext;
36
37pub mod extractors {
38 pub use crate::magic::{Arguments, Identifier, This};
39}
40
41#[derive(Error, Debug, PartialEq, Clone)]
42pub enum ExecutionError {
43 #[error("Invalid argument count: expected {expected}, got {actual}")]
44 InvalidArgumentCount { expected: usize, actual: usize },
45 #[error("Invalid argument type: {:?}", .target)]
46 UnsupportedTargetType { target: Value },
47 #[error("Method '{method}' not supported on type '{target:?}'")]
48 NotSupportedAsMethod { method: String, target: Value },
49 #[error("Unable to use value '{0:?}' as a key")]
52 UnsupportedKeyType(Value),
53 #[error("Unexpected type: got '{got}', want '{want}'")]
54 UnexpectedType { got: String, want: String },
55 #[error("No such key: {0}")]
58 NoSuchKey(Arc<String>),
59 #[error("Undeclared reference to '{0}'")]
62 UndeclaredReference(Arc<String>),
63 #[error("Missing argument or target")]
66 MissingArgumentOrTarget,
67 #[error("{0:?} can not be compared to {1:?}")]
69 ValuesNotComparable(Value, Value),
70 #[error("Unsupported unary operator '{0}': {1:?}")]
72 UnsupportedUnaryOperator(&'static str, Value),
73 #[error("Unsupported binary operator '{0}': {1:?}, {2:?}")]
76 UnsupportedBinaryOperator(&'static str, Value, Value),
77 #[error("Cannot use value as map index: {0:?}")]
79 UnsupportedMapIndex(Value),
80 #[error("Cannot use value as list index: {0:?}")]
82 UnsupportedListIndex(Value),
83 #[error("Cannot use value {0:?} to index {1:?}")]
85 UnsupportedIndex(Value, Value),
86 #[error("Unsupported function call identifier type: {0:?}")]
89 UnsupportedFunctionCallIdentifierType(Expression),
90 #[error("Unsupported fields construction: {0:?}")]
93 UnsupportedFieldsConstruction(Member),
94 #[error("Error executing function '{function}': {message}")]
96 FunctionError { function: String, message: String },
97}
98
99impl ExecutionError {
100 pub fn no_such_key(name: &str) -> Self {
101 ExecutionError::NoSuchKey(Arc::new(name.to_string()))
102 }
103
104 pub fn undeclared_reference(name: &str) -> Self {
105 ExecutionError::UndeclaredReference(Arc::new(name.to_string()))
106 }
107
108 pub fn invalid_argument_count(expected: usize, actual: usize) -> Self {
109 ExecutionError::InvalidArgumentCount { expected, actual }
110 }
111
112 pub fn function_error<E: ToString>(function: &str, error: E) -> Self {
113 ExecutionError::FunctionError {
114 function: function.to_string(),
115 message: error.to_string(),
116 }
117 }
118
119 pub fn unsupported_target_type(target: Value) -> Self {
120 ExecutionError::UnsupportedTargetType { target }
121 }
122
123 pub fn not_supported_as_method(method: &str, target: Value) -> Self {
124 ExecutionError::NotSupportedAsMethod {
125 method: method.to_string(),
126 target,
127 }
128 }
129
130 pub fn unsupported_key_type(value: Value) -> Self {
131 ExecutionError::UnsupportedKeyType(value)
132 }
133
134 pub fn missing_argument_or_target() -> Self {
135 ExecutionError::MissingArgumentOrTarget
136 }
137}
138
139#[derive(Debug)]
140pub struct Program {
141 expression: Expression,
142}
143
144impl Program {
145 pub fn compile(source: &str) -> Result<Program, ParseError> {
146 parse(source).map(|expression| Program { expression })
147 }
148
149 pub fn execute(&self, context: &Context) -> ResolveResult {
150 Value::resolve(&self.expression, context)
151 }
152
153 pub fn references(&self) -> ExpressionReferences {
165 self.expression.references()
166 }
167}
168
169impl TryFrom<&str> for Program {
170 type Error = ParseError;
171
172 fn try_from(value: &str) -> Result<Self, Self::Error> {
173 Program::compile(value)
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use crate::context::Context;
180 use crate::objects::{ResolveResult, Value};
181 use crate::{ExecutionError, Program};
182 use std::collections::HashMap;
183 use std::convert::TryInto;
184
185 pub(crate) fn test_script(script: &str, ctx: Option<Context>) -> ResolveResult {
187 let program = Program::compile(script).unwrap();
188 program.execute(&ctx.unwrap_or_default())
189 }
190
191 #[test]
192 fn parse() {
193 Program::compile("1 + 1").unwrap();
194 }
195
196 #[test]
197 fn from_str() {
198 let input = "1.1";
199 let _p: Program = input.try_into().unwrap();
200 }
201
202 #[test]
203 fn variables() {
204 fn assert_output(script: &str, expected: ResolveResult) {
205 let mut ctx = Context::default();
206 ctx.add_variable_from_value("foo", HashMap::from([("bar", 1i64)]));
207 ctx.add_variable_from_value("arr", vec![1i64, 2, 3]);
208 ctx.add_variable_from_value("str", "foobar".to_string());
209 assert_eq!(test_script(script, Some(ctx)), expected);
210 }
211
212 assert_output("size([1, 2, 3]) == 3", Ok(true.into()));
214 assert_output("size([]) == 3", Ok(false.into()));
215
216 assert_output("foo.bar == 1", Ok(true.into()));
218
219 assert_output("arr[0] == 1", Ok(true.into()));
221
222 assert_output("str[0] == 'f'", Ok(true.into()));
224
225 assert_output(
227 "{'a': 1} + {'a': 2, 'b': 3}",
228 Ok(HashMap::from([("a", 2), ("b", 3)]).into()),
229 );
230 }
231
232 #[test]
233 fn test_execution_errors() {
234 let tests = vec![
235 (
236 "no such key",
237 "foo.baz.bar == 1",
238 ExecutionError::no_such_key("baz"),
239 ),
240 (
241 "undeclared reference",
242 "missing == 1",
243 ExecutionError::undeclared_reference("missing"),
244 ),
245 (
246 "undeclared method",
247 "1.missing()",
248 ExecutionError::undeclared_reference("missing"),
249 ),
250 (
251 "undeclared function",
252 "missing(1)",
253 ExecutionError::undeclared_reference("missing"),
254 ),
255 (
256 "unsupported key type",
257 "{null: true}",
258 ExecutionError::unsupported_key_type(Value::Null),
259 ),
260 ];
261
262 for (name, script, error) in tests {
263 let mut ctx = Context::default();
264 ctx.add_variable_from_value("foo", HashMap::from([("bar", 1)]));
265 let res = test_script(script, Some(ctx));
266 assert_eq!(res, error.into(), "{}", name);
267 }
268 }
269}