1use cel_parser::{ArithmeticOp, Atom, Expression, Member, RelationOp};
2use quote::quote;
3use syn::parse_quote;
4use tinc_cel::CelValue;
5
6use super::{CompileError, CompiledExpr, Compiler, CompilerCtx, ConstantCompiledExpr, RuntimeCompiledExpr};
7use crate::codegen::cel::types::CelType;
8use crate::types::{ProtoModifiedValueType, ProtoType, ProtoValueType};
9
10pub(crate) fn resolve(ctx: &Compiler, expr: &Expression) -> Result<CompiledExpr, CompileError> {
11 match expr {
12 Expression::And(left, right) => resolve_and(ctx, left, right),
13 Expression::Arithmetic(left, op, right) => resolve_arithmetic(ctx, left, op, right),
14 Expression::Atom(atom) => resolve_atom(ctx, atom),
15 Expression::FunctionCall(func, this, args) => resolve_function_call(ctx, func, this.as_deref(), args),
16 Expression::Ident(ident) => resolve_ident(ctx, ident),
17 Expression::List(items) => resolve_list(ctx, items),
18 Expression::Map(items) => resolve_map(ctx, items),
19 Expression::Member(expr, member) => resolve_member(ctx, expr, member),
20 Expression::Or(left, right) => resolve_or(ctx, left, right),
21 Expression::Relation(left, op, right) => resolve_relation(ctx, left, op, right),
22 Expression::Ternary(cond, left, right) => resolve_ternary(ctx, cond, left, right),
23 Expression::Unary(op, expr) => resolve_unary(ctx, op, expr),
24 }
25}
26
27fn resolve_and(ctx: &Compiler, left: &Expression, right: &Expression) -> Result<CompiledExpr, CompileError> {
28 let left = ctx.resolve(left)?.into_bool(ctx);
29 let right = ctx.resolve(right)?.into_bool(ctx);
30 match (left, right) {
31 (
32 CompiledExpr::Constant(ConstantCompiledExpr { value: left }),
33 CompiledExpr::Constant(ConstantCompiledExpr { value: right }),
34 ) => Ok(CompiledExpr::constant(left.to_bool() && right.to_bool())),
35 (CompiledExpr::Constant(ConstantCompiledExpr { value: const_value }), other)
36 | (other, CompiledExpr::Constant(ConstantCompiledExpr { value: const_value })) => {
37 if const_value.to_bool() {
38 Ok(other)
39 } else {
40 Ok(CompiledExpr::constant(false))
41 }
42 }
43 (left, right) => Ok(CompiledExpr::runtime(
44 CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
45 parse_quote! {
46 (#left) && (#right)
47 },
48 )),
49 }
50}
51
52fn resolve_arithmetic(
53 ctx: &Compiler,
54 left: &Expression,
55 op: &ArithmeticOp,
56 right: &Expression,
57) -> Result<CompiledExpr, CompileError> {
58 let left = ctx.resolve(left)?.into_cel()?;
59 let right = ctx.resolve(right)?.into_cel()?;
60 match (left, right) {
61 (
62 CompiledExpr::Constant(ConstantCompiledExpr { value: left }),
63 CompiledExpr::Constant(ConstantCompiledExpr { value: right }),
64 ) => match op {
65 ArithmeticOp::Add => Ok(CompiledExpr::constant(CelValue::cel_add(left, right)?)),
66 ArithmeticOp::Subtract => Ok(CompiledExpr::constant(CelValue::cel_sub(left, right)?)),
67 ArithmeticOp::Divide => Ok(CompiledExpr::constant(CelValue::cel_div(left, right)?)),
68 ArithmeticOp::Multiply => Ok(CompiledExpr::constant(CelValue::cel_mul(left, right)?)),
69 ArithmeticOp::Modulus => Ok(CompiledExpr::constant(CelValue::cel_rem(left, right)?)),
70 },
71 (left, right) => {
72 let op = match op {
73 ArithmeticOp::Add => quote! { cel_add },
74 ArithmeticOp::Subtract => quote! { cel_sub },
75 ArithmeticOp::Divide => quote! { cel_div },
76 ArithmeticOp::Multiply => quote! { cel_mul },
77 ArithmeticOp::Modulus => quote! { cel_rem },
78 };
79
80 Ok(CompiledExpr::runtime(
81 CelType::CelValue,
82 parse_quote! {
83 ::tinc::__private::cel::CelValue::#op(
84 #right,
85 #left,
86 )?
87 },
88 ))
89 }
90 }
91}
92
93fn resolve_atom(_: &Compiler, atom: &Atom) -> Result<CompiledExpr, CompileError> {
94 match atom {
95 Atom::Int(v) => Ok(CompiledExpr::constant(v)),
96 Atom::UInt(v) => Ok(CompiledExpr::constant(v)),
97 Atom::Float(v) => Ok(CompiledExpr::constant(v)),
98 Atom::String(v) => Ok(CompiledExpr::constant(tinc_cel::CelValue::String(v.to_string().into()))),
99 Atom::Bytes(v) => Ok(CompiledExpr::constant(tinc_cel::CelValue::Bytes(v.to_vec().into()))),
100 Atom::Bool(v) => Ok(CompiledExpr::constant(v)),
101 Atom::Null => Ok(CompiledExpr::constant(tinc_cel::CelValue::Null)),
102 }
103}
104
105fn resolve_function_call(
106 ctx: &Compiler,
107 func: &Expression,
108 this: Option<&Expression>,
109 args: &[Expression],
110) -> Result<CompiledExpr, CompileError> {
111 let Expression::Ident(func_name) = func else {
112 return Err(CompileError::UnsupportedFunctionCallIdentifierType(func.clone()));
113 };
114
115 let Some(func) = ctx.get_function(func_name) else {
116 return Err(CompileError::FunctionNotFound(func_name.to_string()));
117 };
118
119 let this = if let Some(this) = this {
120 Some(ctx.resolve(this)?)
121 } else {
122 None
123 };
124
125 func.compile(CompilerCtx::new(ctx.child(), this, args))
126}
127
128fn resolve_ident(ctx: &Compiler, ident: &str) -> Result<CompiledExpr, CompileError> {
129 ctx.get_variable(ident)
130 .cloned()
131 .ok_or_else(|| CompileError::VariableNotFound(ident.to_owned()))
132}
133
134fn resolve_list(ctx: &Compiler, items: &[Expression]) -> Result<CompiledExpr, CompileError> {
135 let items = items
136 .iter()
137 .map(|item| ctx.resolve(item)?.into_cel())
138 .collect::<Result<Vec<_>, _>>()?;
139
140 if items.iter().any(|i| matches!(i, CompiledExpr::Runtime(_))) {
141 Ok(CompiledExpr::runtime(
142 CelType::CelValue,
143 parse_quote! {
144 ::tinc::__private::cel::CelValue::List(::std::iter::FromIterator::from_iter([
145 #(#items),*
146 ]))
147 },
148 ))
149 } else {
150 Ok(CompiledExpr::constant(CelValue::List(
151 items
152 .into_iter()
153 .map(|item| match item {
154 CompiledExpr::Constant(ConstantCompiledExpr { value }) => value,
155 _ => unreachable!(),
156 })
157 .collect(),
158 )))
159 }
160}
161
162fn resolve_map(ctx: &Compiler, items: &[(Expression, Expression)]) -> Result<CompiledExpr, CompileError> {
163 let items = items
164 .iter()
165 .map(|(key, value)| {
166 let key = ctx.resolve(key)?.into_cel()?;
167 let value = ctx.resolve(value)?.into_cel()?;
168 Ok((key, value))
169 })
170 .collect::<Result<Vec<_>, CompileError>>()?;
171
172 if items
173 .iter()
174 .any(|(key, value)| matches!(key, CompiledExpr::Runtime(_)) || matches!(value, CompiledExpr::Runtime(_)))
175 {
176 let items = items.into_iter().map(|(key, value)| quote!((#key, #value)));
177 Ok(CompiledExpr::runtime(
178 CelType::CelValue,
179 parse_quote! {
180 ::tinc::__private::cel::CelValue::Map(::std::iter::FromIterator::from_iter([
181 #(#items),*
182 ]))
183 },
184 ))
185 } else {
186 Ok(CompiledExpr::constant(CelValue::Map(
187 items
188 .into_iter()
189 .map(|(key, value)| match (key, value) {
190 (
191 CompiledExpr::Constant(ConstantCompiledExpr { value: key }),
192 CompiledExpr::Constant(ConstantCompiledExpr { value }),
193 ) => (key, value),
194 _ => unreachable!(),
195 })
196 .collect(),
197 )))
198 }
199}
200
201fn resolve_member(ctx: &Compiler, expr: &Expression, member: &Member) -> Result<CompiledExpr, CompileError> {
202 let expr = ctx.resolve(expr)?;
203 match member {
204 Member::Attribute(attr) => {
205 let attr = attr.as_str();
206 match &expr {
207 CompiledExpr::Runtime(RuntimeCompiledExpr {
208 expr,
209 ty: CelType::CelValue,
210 }) => Ok(CompiledExpr::runtime(
211 CelType::CelValue,
212 parse_quote! {
213 ::tinc::__private::cel::CelValue::access(
214 #expr,
215 #attr
216 )?
217 },
218 )),
219 CompiledExpr::Runtime(RuntimeCompiledExpr {
220 expr,
221 ty:
222 ty @ CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Optional(ProtoValueType::Message(
223 full_name,
224 )))),
225 }) => {
226 let msg = ctx
227 .registry()
228 .get_message(full_name)
229 .ok_or_else(|| CompileError::MissingMessage(full_name.clone()))?;
230
231 let field_ty = msg.fields.get(attr).ok_or_else(|| CompileError::MemberAccess {
232 ty: Box::new(ty.clone()),
233 message: format!("message {} does not have field {}", msg.full_name, attr),
234 })?;
235
236 let field_ident = field_ty.rust_ident();
237
238 Ok(CompiledExpr::runtime(
239 CelType::Proto(field_ty.ty.clone()),
240 parse_quote! {
241 match (#expr) {
242 Some(value) => &value.#field_ident,
243 None => return Err(::tinc::__private::cel::CelError::BadAccess {
244 member: ::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed(#attr)),
245 container: ::tinc::__private::cel::CelValue::Null,
246 }),
247 }
248 },
249 ))
250 }
251 CompiledExpr::Runtime(RuntimeCompiledExpr {
252 expr,
253 ty: ty @ CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::OneOf(oneof))),
254 }) => {
255 let field_ty = oneof.fields.get(attr).ok_or_else(|| CompileError::MemberAccess {
256 ty: Box::new(ty.clone()),
257 message: format!("oneof {} does not have field {}", oneof.full_name, attr),
258 })?;
259
260 let field_ident = field_ty.rust_ident();
261
262 Ok(CompiledExpr::runtime(
263 CelType::Proto(ProtoType::Value(field_ty.ty.clone())),
264 parse_quote! {
265 match (#expr) {
266 Some(value) => &value.#field_ident,
267 None => return Err(::tinc::__private::cel::CelError::BadAccess {
268 member: ::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed(#attr)),
269 container: ::tinc::__private::cel::CelValue::Null,
270 }),
271 }
272 },
273 ))
274 }
275 CompiledExpr::Runtime(RuntimeCompiledExpr {
276 expr,
277 ty: ty @ CelType::Proto(ProtoType::Value(ProtoValueType::Message(full_name))),
278 }) => {
279 let msg = ctx
280 .registry()
281 .get_message(full_name)
282 .ok_or_else(|| CompileError::MissingMessage(full_name.clone()))?;
283 let field_ty = msg.fields.get(attr).ok_or_else(|| CompileError::MemberAccess {
284 ty: Box::new(ty.clone()),
285 message: format!("message {} does not have field {}", msg.full_name, attr),
286 })?;
287
288 let field_ident = field_ty.rust_ident();
289
290 Ok(CompiledExpr::runtime(
291 CelType::Proto(field_ty.ty.clone()),
292 parse_quote! {
293 &(#expr).#field_ident,
294 },
295 ))
296 }
297 CompiledExpr::Runtime(RuntimeCompiledExpr {
298 expr,
299 ty: CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Map(ProtoValueType::String, value_ty))),
300 }) => Ok(CompiledExpr::runtime(
301 CelType::Proto(ProtoType::Value(value_ty.clone())),
302 parse_quote! {
303 ::tinc::__private::cel::map_access(
304 #expr,
305 #attr,
306 )?
307 },
308 )),
309 CompiledExpr::Runtime(RuntimeCompiledExpr { ty, .. }) => Err(CompileError::MemberAccess {
310 ty: Box::new(ty.clone()),
311 message: "can only access attributes on messages and maps with string keys".to_string(),
312 }),
313 CompiledExpr::Constant(ConstantCompiledExpr { value: container }) => {
314 Ok(CompiledExpr::constant(tinc_cel::CelValue::cel_access(container, attr)?))
315 }
316 }
317 }
318 Member::Index(idx) => {
319 let idx = ctx.resolve(idx)?.into_cel()?;
320 match (expr, idx) {
321 (
322 expr @ CompiledExpr::Runtime(RuntimeCompiledExpr {
323 ty: CelType::CelValue, ..
324 }),
325 idx,
326 )
327 | (expr @ CompiledExpr::Constant(_), idx @ CompiledExpr::Runtime(_)) => Ok(CompiledExpr::runtime(
328 CelType::CelValue,
329 parse_quote! {
330 ::tinc::__private::cel::CelValue::cel_access(#expr, #idx)?
331 },
332 )),
333 (
334 CompiledExpr::Runtime(RuntimeCompiledExpr {
335 expr,
336 ty: CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Repeated(item_ty))),
337 }),
338 idx,
339 ) => Ok(CompiledExpr::runtime(
340 CelType::Proto(ProtoType::Value(item_ty.clone())),
341 parse_quote! {
342 ::tinc::__private::cel::CelValueConv::array_access(
343 #expr,
344 #idx,
345 )?
346 },
347 )),
348 (
349 CompiledExpr::Runtime(RuntimeCompiledExpr {
350 expr,
351 ty: CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Map(_, value_ty))),
352 }),
353 idx,
354 ) => Ok(CompiledExpr::runtime(
355 CelType::Proto(ProtoType::Value(value_ty.clone())),
356 parse_quote! {
357 ::tinc::__private::cel::map_access(
358 #expr,
359 #idx,
360 )?
361 },
362 )),
363 (CompiledExpr::Runtime(RuntimeCompiledExpr { ty, .. }), _) => Err(CompileError::MemberAccess {
364 ty: Box::new(ty.clone()),
365 message: "cannot index into non-repeated and non-map values".to_string(),
366 }),
367 (
368 CompiledExpr::Constant(ConstantCompiledExpr { value: container }),
369 CompiledExpr::Constant(ConstantCompiledExpr { value: idx }),
370 ) => Ok(CompiledExpr::constant(tinc_cel::CelValue::cel_access(container, idx)?)),
371 }
372 }
373 Member::Fields(_) => Err(CompileError::NotImplemented),
374 }
375}
376
377fn resolve_or(ctx: &Compiler, left: &Expression, right: &Expression) -> Result<CompiledExpr, CompileError> {
378 let left = ctx.resolve(left)?.into_bool(ctx);
379 let right = ctx.resolve(right)?.into_bool(ctx);
380 match (left, right) {
381 (
382 CompiledExpr::Constant(ConstantCompiledExpr { value: left }),
383 CompiledExpr::Constant(ConstantCompiledExpr { value: right }),
384 ) => Ok(CompiledExpr::constant(left.to_bool() || right.to_bool())),
385 (CompiledExpr::Constant(ConstantCompiledExpr { value: const_value }), other)
386 | (other, CompiledExpr::Constant(ConstantCompiledExpr { value: const_value })) => {
387 if const_value.to_bool() {
388 Ok(CompiledExpr::constant(true))
389 } else {
390 Ok(other)
391 }
392 }
393 (left, right) => Ok(CompiledExpr::runtime(
394 CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
395 parse_quote! {
396 (#left) || (#right)
397 },
398 )),
399 }
400}
401
402fn resolve_relation(
403 ctx: &Compiler,
404 left: &Expression,
405 op: &RelationOp,
406 right: &Expression,
407) -> Result<CompiledExpr, CompileError> {
408 let left = ctx.resolve(left)?.into_cel()?;
409 let right = ctx.resolve(right)?;
410 if let (
411 RelationOp::In,
412 CompiledExpr::Runtime(RuntimeCompiledExpr {
413 ty:
414 right_ty @ CelType::Proto(ProtoType::Modified(
415 ProtoModifiedValueType::Repeated(item) | ProtoModifiedValueType::Map(item, _),
416 )),
417 ..
418 }),
419 ) = (op, &right)
420 && !matches!(item, ProtoValueType::Message { .. })
421 {
422 let op = match &right_ty {
423 CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Repeated(_))) => {
424 quote! { array_contains }
425 }
426 CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Map(_, _))) => quote! { map_contains },
427 _ => unreachable!(),
428 };
429
430 return Ok(CompiledExpr::runtime(
431 CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
432 parse_quote! {
433 ::tinc::__private::cel::#op(
434 #right,
435 #left,
436 )
437 },
438 ));
439 }
440
441 let right = right.into_cel()?;
442
443 match (left, right) {
444 (
445 CompiledExpr::Constant(ConstantCompiledExpr { value: left }),
446 CompiledExpr::Constant(ConstantCompiledExpr { value: right }),
447 ) => match op {
448 RelationOp::LessThan => Ok(CompiledExpr::constant(CelValue::cel_lt(left, right)?)),
449 RelationOp::LessThanEq => Ok(CompiledExpr::constant(CelValue::cel_lte(left, right)?)),
450 RelationOp::GreaterThan => Ok(CompiledExpr::constant(CelValue::cel_gt(left, right)?)),
451 RelationOp::GreaterThanEq => Ok(CompiledExpr::constant(CelValue::cel_gte(left, right)?)),
452 RelationOp::Equals => Ok(CompiledExpr::constant(CelValue::cel_eq(left, right)?)),
453 RelationOp::NotEquals => Ok(CompiledExpr::constant(CelValue::cel_neq(left, right)?)),
454 RelationOp::In => Ok(CompiledExpr::constant(CelValue::cel_in(left, right)?)),
455 },
456 (left, right) => {
457 let op = match op {
458 RelationOp::LessThan => quote! { cel_lt },
459 RelationOp::LessThanEq => quote! { cel_lte },
460 RelationOp::GreaterThan => quote! { cel_gt },
461 RelationOp::GreaterThanEq => quote! { cel_gte },
462 RelationOp::Equals => quote! { cel_eq },
463 RelationOp::NotEquals => quote! { cel_neq },
464 RelationOp::In => quote! { cel_in },
465 };
466
467 Ok(CompiledExpr::runtime(
468 CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
469 parse_quote! {
470 ::tinc::__private::cel::CelValue::#op(
471 #left,
472 #right,
473 )?
474 },
475 ))
476 }
477 }
478}
479
480fn resolve_ternary(
481 ctx: &Compiler,
482 cond: &Expression,
483 left: &Expression,
484 right: &Expression,
485) -> Result<CompiledExpr, CompileError> {
486 let cond = ctx.resolve(cond)?.into_bool(ctx);
487 let left = ctx.resolve(left)?.into_cel()?;
488 let right = ctx.resolve(right)?.into_cel()?;
489
490 match cond {
491 CompiledExpr::Constant(ConstantCompiledExpr { value: cond }) => {
492 if cond.to_bool() {
493 Ok(left)
494 } else {
495 Ok(right)
496 }
497 }
498 cond => Ok(CompiledExpr::runtime(
499 CelType::CelValue,
500 parse_quote! {
501 if (#cond) {
502 #left
503 } else {
504 #right
505 }
506 },
507 )),
508 }
509}
510
511fn resolve_unary(ctx: &Compiler, op: &cel_parser::UnaryOp, expr: &Expression) -> Result<CompiledExpr, CompileError> {
512 let expr = ctx.resolve(expr)?;
513 match op {
514 cel_parser::UnaryOp::Not => {
515 let expr = expr.into_bool(ctx);
516 match expr {
517 CompiledExpr::Constant(ConstantCompiledExpr { value: expr }) => Ok(CompiledExpr::constant(!expr.to_bool())),
518 expr => Ok(CompiledExpr::runtime(
519 CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
520 parse_quote! {
521 !(::tinc::__private::cel::to_bool(#expr))
522 },
523 )),
524 }
525 }
526 cel_parser::UnaryOp::DoubleNot => Ok(expr.into_bool(ctx)),
527 cel_parser::UnaryOp::Minus => {
528 let expr = expr.into_cel()?;
529 match expr {
530 CompiledExpr::Constant(ConstantCompiledExpr { value: expr }) => {
531 Ok(CompiledExpr::constant(CelValue::cel_neg(expr)?))
532 }
533 expr => Ok(CompiledExpr::runtime(
534 CelType::CelValue,
535 parse_quote! {
536 ::tinc::__private::cel::CelValue::cel_neg(#expr)?
537 },
538 )),
539 }
540 }
541 cel_parser::UnaryOp::DoubleMinus => Ok(expr),
542 }
543}
544
545#[cfg(test)]
546#[cfg(feature = "prost")]
547#[cfg_attr(coverage_nightly, coverage(off))]
548mod tests {
549 use cel_parser::parse as parse_cel;
550
551 use super::*;
552 use crate::extern_paths::ExternPaths;
553 use crate::path_set::PathSet;
554 use crate::types::ProtoTypeRegistry;
555
556 #[test]
557 fn test_resolve_atom_int() {
558 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
559 let compiler = Compiler::new(®istry);
560 let expr = parse_cel("1").unwrap();
561 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
562 Ok(
563 Constant(
564 ConstantCompiledExpr {
565 value: Number(
566 I64(
567 1,
568 ),
569 ),
570 },
571 ),
572 )
573 ");
574 }
575
576 #[test]
577 fn test_resolve_atom_uint() {
578 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
579 let compiler = Compiler::new(®istry);
580 let expr = parse_cel("3u").unwrap();
581 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
582 Ok(
583 Constant(
584 ConstantCompiledExpr {
585 value: Number(
586 U64(
587 3,
588 ),
589 ),
590 },
591 ),
592 )
593 ");
594 }
595
596 #[test]
597 fn test_resolve_atom_float() {
598 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
599 let compiler = Compiler::new(®istry);
600 let expr = parse_cel("1.23").unwrap();
601 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
602 Ok(
603 Constant(
604 ConstantCompiledExpr {
605 value: Number(
606 F64(
607 1.23,
608 ),
609 ),
610 },
611 ),
612 )
613 ");
614 }
615
616 #[test]
617 fn test_resolve_atom_string_bytes_bool_null() {
618 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
619 let compiler = Compiler::new(®istry);
620
621 let expr_str = parse_cel("\"foo\"").unwrap();
622 insta::assert_debug_snapshot!(resolve(&compiler, &expr_str), @r#"
623 Ok(
624 Constant(
625 ConstantCompiledExpr {
626 value: String(
627 Owned(
628 "foo",
629 ),
630 ),
631 },
632 ),
633 )
634 "#);
635
636 let expr_bytes = parse_cel("b\"hi\"").unwrap();
637 insta::assert_debug_snapshot!(resolve(&compiler, &expr_bytes), @r#"
638 Ok(
639 Constant(
640 ConstantCompiledExpr {
641 value: Bytes(
642 Owned(
643 b"hi",
644 ),
645 ),
646 },
647 ),
648 )
649 "#);
650
651 let expr_bool = parse_cel("true").unwrap();
652 insta::assert_debug_snapshot!(resolve(&compiler, &expr_bool), @r"
653 Ok(
654 Constant(
655 ConstantCompiledExpr {
656 value: Bool(
657 true,
658 ),
659 },
660 ),
661 )
662 ");
663
664 let expr_null = parse_cel("null").unwrap();
665 insta::assert_debug_snapshot!(resolve(&compiler, &expr_null), @r"
666 Ok(
667 Constant(
668 ConstantCompiledExpr {
669 value: Null,
670 },
671 ),
672 )
673 ");
674 }
675
676 #[test]
677 fn test_resolve_arithmetic_constant() {
678 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
679 let compiler = Compiler::new(®istry);
680
681 let expr = parse_cel("10 + 5").unwrap();
682 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
683 Ok(
684 Constant(
685 ConstantCompiledExpr {
686 value: Number(
687 I64(
688 15,
689 ),
690 ),
691 },
692 ),
693 )
694 ");
695
696 let expr = parse_cel("10 - 4").unwrap();
697 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
698 Ok(
699 Constant(
700 ConstantCompiledExpr {
701 value: Number(
702 I64(
703 6,
704 ),
705 ),
706 },
707 ),
708 )
709 ");
710
711 let expr = parse_cel("6 * 7").unwrap();
712 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
713 Ok(
714 Constant(
715 ConstantCompiledExpr {
716 value: Number(
717 I64(
718 42,
719 ),
720 ),
721 },
722 ),
723 )
724 ");
725
726 let expr = parse_cel("20 / 4").unwrap();
727 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
728 Ok(
729 Constant(
730 ConstantCompiledExpr {
731 value: Number(
732 I64(
733 5,
734 ),
735 ),
736 },
737 ),
738 )
739 ");
740
741 let expr = parse_cel("10 % 3").unwrap();
742 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
743 Ok(
744 Constant(
745 ConstantCompiledExpr {
746 value: Number(
747 I64(
748 1,
749 ),
750 ),
751 },
752 ),
753 )
754 ");
755 }
756
757 #[test]
758 fn test_resolve_relation_constant() {
759 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
760 let compiler = Compiler::new(®istry);
761
762 let expr = parse_cel("1 < 2").unwrap();
763 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
764 Ok(
765 Constant(
766 ConstantCompiledExpr {
767 value: Bool(
768 true,
769 ),
770 },
771 ),
772 )
773 ");
774 let expr = parse_cel("1 <= 1").unwrap();
775 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
776 Ok(
777 Constant(
778 ConstantCompiledExpr {
779 value: Bool(
780 true,
781 ),
782 },
783 ),
784 )
785 ");
786 let expr = parse_cel("2 > 1").unwrap();
787 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
788 Ok(
789 Constant(
790 ConstantCompiledExpr {
791 value: Bool(
792 true,
793 ),
794 },
795 ),
796 )
797 ");
798 let expr = parse_cel("2 >= 2").unwrap();
799 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
800 Ok(
801 Constant(
802 ConstantCompiledExpr {
803 value: Bool(
804 true,
805 ),
806 },
807 ),
808 )
809 ");
810 let expr = parse_cel("1 == 1").unwrap();
811 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
812 Ok(
813 Constant(
814 ConstantCompiledExpr {
815 value: Bool(
816 true,
817 ),
818 },
819 ),
820 )
821 ");
822 let expr = parse_cel("1 != 2").unwrap();
823 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
824 Ok(
825 Constant(
826 ConstantCompiledExpr {
827 value: Bool(
828 true,
829 ),
830 },
831 ),
832 )
833 ");
834 let expr = parse_cel("1 in [1, 2, 3]").unwrap();
835 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
836 Ok(
837 Constant(
838 ConstantCompiledExpr {
839 value: Bool(
840 true,
841 ),
842 },
843 ),
844 )
845 ");
846 }
847
848 #[test]
849 fn test_resolve_boolean_constant() {
850 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
851 let compiler = Compiler::new(®istry);
852
853 let expr_and = parse_cel("true && false").unwrap();
854 insta::assert_debug_snapshot!(resolve(&compiler, &expr_and), @r"
855 Ok(
856 Constant(
857 ConstantCompiledExpr {
858 value: Bool(
859 false,
860 ),
861 },
862 ),
863 )
864 ");
865
866 let expr_or = parse_cel("true || false").unwrap();
867 insta::assert_debug_snapshot!(resolve(&compiler, &expr_or), @r"
868 Ok(
869 Constant(
870 ConstantCompiledExpr {
871 value: Bool(
872 true,
873 ),
874 },
875 ),
876 )
877 ");
878 }
879
880 #[test]
881 fn test_resolve_unary_constant() {
882 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
883 let compiler = Compiler::new(®istry);
884
885 let expr_not = parse_cel("!false").unwrap();
886 insta::assert_debug_snapshot!(resolve(&compiler, &expr_not), @r"
887 Ok(
888 Constant(
889 ConstantCompiledExpr {
890 value: Bool(
891 true,
892 ),
893 },
894 ),
895 )
896 ");
897
898 let expr_double_not = parse_cel("!!true").unwrap();
899 insta::assert_debug_snapshot!(resolve(&compiler, &expr_double_not), @r"
900 Ok(
901 Constant(
902 ConstantCompiledExpr {
903 value: Bool(
904 true,
905 ),
906 },
907 ),
908 )
909 ");
910
911 let expr_neg = parse_cel("-5").unwrap();
912 insta::assert_debug_snapshot!(resolve(&compiler, &expr_neg), @r"
913 Ok(
914 Constant(
915 ConstantCompiledExpr {
916 value: Number(
917 I64(
918 -5,
919 ),
920 ),
921 },
922 ),
923 )
924 ");
925
926 let expr_double_neg = parse_cel("--5").unwrap();
927 insta::assert_debug_snapshot!(resolve(&compiler, &expr_double_neg), @r"
928 Ok(
929 Constant(
930 ConstantCompiledExpr {
931 value: Number(
932 I64(
933 5,
934 ),
935 ),
936 },
937 ),
938 )
939 ");
940 }
941
942 #[test]
943 fn test_resolve_ternary_constant() {
944 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
945 let compiler = Compiler::new(®istry);
946
947 let expr_true = parse_cel("true ? 1 : 2").unwrap();
948 insta::assert_debug_snapshot!(resolve(&compiler, &expr_true), @r"
949 Ok(
950 Constant(
951 ConstantCompiledExpr {
952 value: Number(
953 I64(
954 1,
955 ),
956 ),
957 },
958 ),
959 )
960 ");
961
962 let expr_false = parse_cel("false ? 1 : 2").unwrap();
963 insta::assert_debug_snapshot!(resolve(&compiler, &expr_false), @r"
964 Ok(
965 Constant(
966 ConstantCompiledExpr {
967 value: Number(
968 I64(
969 2,
970 ),
971 ),
972 },
973 ),
974 )
975 ");
976 }
977
978 #[test]
979 fn test_resolve_list_map_constant() {
980 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
981 let compiler = Compiler::new(®istry);
982
983 let expr_list = parse_cel("[1, 2, 3]").unwrap();
984 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
985 Ok(
986 Constant(
987 ConstantCompiledExpr {
988 value: List(
989 [
990 Number(
991 I64(
992 1,
993 ),
994 ),
995 Number(
996 I64(
997 2,
998 ),
999 ),
1000 Number(
1001 I64(
1002 3,
1003 ),
1004 ),
1005 ],
1006 ),
1007 },
1008 ),
1009 )
1010 ");
1011
1012 let expr_map = parse_cel("{'a': 1, 'b': 2}").unwrap();
1013 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r#"
1014 Ok(
1015 Constant(
1016 ConstantCompiledExpr {
1017 value: Map(
1018 [
1019 (
1020 String(
1021 Owned(
1022 "a",
1023 ),
1024 ),
1025 Number(
1026 I64(
1027 1,
1028 ),
1029 ),
1030 ),
1031 (
1032 String(
1033 Owned(
1034 "b",
1035 ),
1036 ),
1037 Number(
1038 I64(
1039 2,
1040 ),
1041 ),
1042 ),
1043 ],
1044 ),
1045 },
1046 ),
1047 )
1048 "#);
1049 }
1050
1051 #[test]
1052 fn test_resolve_negative_variable() {
1053 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
1054 let mut compiler = Compiler::new(®istry);
1055
1056 compiler.add_variable("x", CompiledExpr::constant(CelValue::Number(1.into())));
1057
1058 let expr_list = parse_cel("-x").unwrap();
1059 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
1060 Ok(
1061 Constant(
1062 ConstantCompiledExpr {
1063 value: Number(
1064 I64(
1065 -1,
1066 ),
1067 ),
1068 },
1069 ),
1070 )
1071 ");
1072 }
1073
1074 #[test]
1075 fn test_resolve_access() {
1076 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
1077 let compiler = Compiler::new(®istry);
1078
1079 let expr_list = parse_cel("[1, 2, 3][2]").unwrap();
1080 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
1081 Ok(
1082 Constant(
1083 ConstantCompiledExpr {
1084 value: Number(
1085 I64(
1086 3,
1087 ),
1088 ),
1089 },
1090 ),
1091 )
1092 ");
1093
1094 let expr_map = parse_cel("({'a': 1, 'b': 2}).a").unwrap();
1095 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r"
1096 Ok(
1097 Constant(
1098 ConstantCompiledExpr {
1099 value: Number(
1100 I64(
1101 1,
1102 ),
1103 ),
1104 },
1105 ),
1106 )
1107 ");
1108
1109 let expr_map = parse_cel("({'a': 1, 'b': 2})['b']").unwrap();
1110 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r"
1111 Ok(
1112 Constant(
1113 ConstantCompiledExpr {
1114 value: Number(
1115 I64(
1116 2,
1117 ),
1118 ),
1119 },
1120 ),
1121 )
1122 ");
1123 }
1124}