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::types::ProtoTypeRegistry;
553
554 #[test]
555 fn test_resolve_atom_int() {
556 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
557 let compiler = Compiler::new(®istry);
558 let expr = parse_cel("1").unwrap();
559 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
560 Ok(
561 Constant(
562 ConstantCompiledExpr {
563 value: Number(
564 I64(
565 1,
566 ),
567 ),
568 },
569 ),
570 )
571 ");
572 }
573
574 #[test]
575 fn test_resolve_atom_uint() {
576 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
577 let compiler = Compiler::new(®istry);
578 let expr = parse_cel("3u").unwrap();
579 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
580 Ok(
581 Constant(
582 ConstantCompiledExpr {
583 value: Number(
584 U64(
585 3,
586 ),
587 ),
588 },
589 ),
590 )
591 ");
592 }
593
594 #[test]
595 fn test_resolve_atom_float() {
596 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
597 let compiler = Compiler::new(®istry);
598 let expr = parse_cel("1.23").unwrap();
599 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
600 Ok(
601 Constant(
602 ConstantCompiledExpr {
603 value: Number(
604 F64(
605 1.23,
606 ),
607 ),
608 },
609 ),
610 )
611 ");
612 }
613
614 #[test]
615 fn test_resolve_atom_string_bytes_bool_null() {
616 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
617 let compiler = Compiler::new(®istry);
618
619 let expr_str = parse_cel("\"foo\"").unwrap();
620 insta::assert_debug_snapshot!(resolve(&compiler, &expr_str), @r#"
621 Ok(
622 Constant(
623 ConstantCompiledExpr {
624 value: String(
625 Owned(
626 "foo",
627 ),
628 ),
629 },
630 ),
631 )
632 "#);
633
634 let expr_bytes = parse_cel("b\"hi\"").unwrap();
635 insta::assert_debug_snapshot!(resolve(&compiler, &expr_bytes), @r#"
636 Ok(
637 Constant(
638 ConstantCompiledExpr {
639 value: Bytes(
640 Owned(
641 b"hi",
642 ),
643 ),
644 },
645 ),
646 )
647 "#);
648
649 let expr_bool = parse_cel("true").unwrap();
650 insta::assert_debug_snapshot!(resolve(&compiler, &expr_bool), @r"
651 Ok(
652 Constant(
653 ConstantCompiledExpr {
654 value: Bool(
655 true,
656 ),
657 },
658 ),
659 )
660 ");
661
662 let expr_null = parse_cel("null").unwrap();
663 insta::assert_debug_snapshot!(resolve(&compiler, &expr_null), @r"
664 Ok(
665 Constant(
666 ConstantCompiledExpr {
667 value: Null,
668 },
669 ),
670 )
671 ");
672 }
673
674 #[test]
675 fn test_resolve_arithmetic_constant() {
676 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
677 let compiler = Compiler::new(®istry);
678
679 let expr = parse_cel("10 + 5").unwrap();
680 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
681 Ok(
682 Constant(
683 ConstantCompiledExpr {
684 value: Number(
685 I64(
686 15,
687 ),
688 ),
689 },
690 ),
691 )
692 ");
693
694 let expr = parse_cel("10 - 4").unwrap();
695 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
696 Ok(
697 Constant(
698 ConstantCompiledExpr {
699 value: Number(
700 I64(
701 6,
702 ),
703 ),
704 },
705 ),
706 )
707 ");
708
709 let expr = parse_cel("6 * 7").unwrap();
710 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
711 Ok(
712 Constant(
713 ConstantCompiledExpr {
714 value: Number(
715 I64(
716 42,
717 ),
718 ),
719 },
720 ),
721 )
722 ");
723
724 let expr = parse_cel("20 / 4").unwrap();
725 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
726 Ok(
727 Constant(
728 ConstantCompiledExpr {
729 value: Number(
730 I64(
731 5,
732 ),
733 ),
734 },
735 ),
736 )
737 ");
738
739 let expr = parse_cel("10 % 3").unwrap();
740 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
741 Ok(
742 Constant(
743 ConstantCompiledExpr {
744 value: Number(
745 I64(
746 1,
747 ),
748 ),
749 },
750 ),
751 )
752 ");
753 }
754
755 #[test]
756 fn test_resolve_relation_constant() {
757 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
758 let compiler = Compiler::new(®istry);
759
760 let expr = parse_cel("1 < 2").unwrap();
761 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
762 Ok(
763 Constant(
764 ConstantCompiledExpr {
765 value: Bool(
766 true,
767 ),
768 },
769 ),
770 )
771 ");
772 let expr = parse_cel("1 <= 1").unwrap();
773 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
774 Ok(
775 Constant(
776 ConstantCompiledExpr {
777 value: Bool(
778 true,
779 ),
780 },
781 ),
782 )
783 ");
784 let expr = parse_cel("2 > 1").unwrap();
785 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
786 Ok(
787 Constant(
788 ConstantCompiledExpr {
789 value: Bool(
790 true,
791 ),
792 },
793 ),
794 )
795 ");
796 let expr = parse_cel("2 >= 2").unwrap();
797 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
798 Ok(
799 Constant(
800 ConstantCompiledExpr {
801 value: Bool(
802 true,
803 ),
804 },
805 ),
806 )
807 ");
808 let expr = parse_cel("1 == 1").unwrap();
809 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
810 Ok(
811 Constant(
812 ConstantCompiledExpr {
813 value: Bool(
814 true,
815 ),
816 },
817 ),
818 )
819 ");
820 let expr = parse_cel("1 != 2").unwrap();
821 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
822 Ok(
823 Constant(
824 ConstantCompiledExpr {
825 value: Bool(
826 true,
827 ),
828 },
829 ),
830 )
831 ");
832 let expr = parse_cel("1 in [1, 2, 3]").unwrap();
833 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
834 Ok(
835 Constant(
836 ConstantCompiledExpr {
837 value: Bool(
838 true,
839 ),
840 },
841 ),
842 )
843 ");
844 }
845
846 #[test]
847 fn test_resolve_boolean_constant() {
848 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
849 let compiler = Compiler::new(®istry);
850
851 let expr_and = parse_cel("true && false").unwrap();
852 insta::assert_debug_snapshot!(resolve(&compiler, &expr_and), @r"
853 Ok(
854 Constant(
855 ConstantCompiledExpr {
856 value: Bool(
857 false,
858 ),
859 },
860 ),
861 )
862 ");
863
864 let expr_or = parse_cel("true || false").unwrap();
865 insta::assert_debug_snapshot!(resolve(&compiler, &expr_or), @r"
866 Ok(
867 Constant(
868 ConstantCompiledExpr {
869 value: Bool(
870 true,
871 ),
872 },
873 ),
874 )
875 ");
876 }
877
878 #[test]
879 fn test_resolve_unary_constant() {
880 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
881 let compiler = Compiler::new(®istry);
882
883 let expr_not = parse_cel("!false").unwrap();
884 insta::assert_debug_snapshot!(resolve(&compiler, &expr_not), @r"
885 Ok(
886 Constant(
887 ConstantCompiledExpr {
888 value: Bool(
889 true,
890 ),
891 },
892 ),
893 )
894 ");
895
896 let expr_double_not = parse_cel("!!true").unwrap();
897 insta::assert_debug_snapshot!(resolve(&compiler, &expr_double_not), @r"
898 Ok(
899 Constant(
900 ConstantCompiledExpr {
901 value: Bool(
902 true,
903 ),
904 },
905 ),
906 )
907 ");
908
909 let expr_neg = parse_cel("-5").unwrap();
910 insta::assert_debug_snapshot!(resolve(&compiler, &expr_neg), @r"
911 Ok(
912 Constant(
913 ConstantCompiledExpr {
914 value: Number(
915 I64(
916 -5,
917 ),
918 ),
919 },
920 ),
921 )
922 ");
923
924 let expr_double_neg = parse_cel("--5").unwrap();
925 insta::assert_debug_snapshot!(resolve(&compiler, &expr_double_neg), @r"
926 Ok(
927 Constant(
928 ConstantCompiledExpr {
929 value: Number(
930 I64(
931 5,
932 ),
933 ),
934 },
935 ),
936 )
937 ");
938 }
939
940 #[test]
941 fn test_resolve_ternary_constant() {
942 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
943 let compiler = Compiler::new(®istry);
944
945 let expr_true = parse_cel("true ? 1 : 2").unwrap();
946 insta::assert_debug_snapshot!(resolve(&compiler, &expr_true), @r"
947 Ok(
948 Constant(
949 ConstantCompiledExpr {
950 value: Number(
951 I64(
952 1,
953 ),
954 ),
955 },
956 ),
957 )
958 ");
959
960 let expr_false = parse_cel("false ? 1 : 2").unwrap();
961 insta::assert_debug_snapshot!(resolve(&compiler, &expr_false), @r"
962 Ok(
963 Constant(
964 ConstantCompiledExpr {
965 value: Number(
966 I64(
967 2,
968 ),
969 ),
970 },
971 ),
972 )
973 ");
974 }
975
976 #[test]
977 fn test_resolve_list_map_constant() {
978 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
979 let compiler = Compiler::new(®istry);
980
981 let expr_list = parse_cel("[1, 2, 3]").unwrap();
982 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
983 Ok(
984 Constant(
985 ConstantCompiledExpr {
986 value: List(
987 [
988 Number(
989 I64(
990 1,
991 ),
992 ),
993 Number(
994 I64(
995 2,
996 ),
997 ),
998 Number(
999 I64(
1000 3,
1001 ),
1002 ),
1003 ],
1004 ),
1005 },
1006 ),
1007 )
1008 ");
1009
1010 let expr_map = parse_cel("{'a': 1, 'b': 2}").unwrap();
1011 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r#"
1012 Ok(
1013 Constant(
1014 ConstantCompiledExpr {
1015 value: Map(
1016 [
1017 (
1018 String(
1019 Owned(
1020 "a",
1021 ),
1022 ),
1023 Number(
1024 I64(
1025 1,
1026 ),
1027 ),
1028 ),
1029 (
1030 String(
1031 Owned(
1032 "b",
1033 ),
1034 ),
1035 Number(
1036 I64(
1037 2,
1038 ),
1039 ),
1040 ),
1041 ],
1042 ),
1043 },
1044 ),
1045 )
1046 "#);
1047 }
1048
1049 #[test]
1050 fn test_resolve_negative_variable() {
1051 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
1052 let mut compiler = Compiler::new(®istry);
1053
1054 compiler.add_variable("x", CompiledExpr::constant(CelValue::Number(1.into())));
1055
1056 let expr_list = parse_cel("-x").unwrap();
1057 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
1058 Ok(
1059 Constant(
1060 ConstantCompiledExpr {
1061 value: Number(
1062 I64(
1063 -1,
1064 ),
1065 ),
1066 },
1067 ),
1068 )
1069 ");
1070 }
1071
1072 #[test]
1073 fn test_resolve_access() {
1074 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
1075 let compiler = Compiler::new(®istry);
1076
1077 let expr_list = parse_cel("[1, 2, 3][2]").unwrap();
1078 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
1079 Ok(
1080 Constant(
1081 ConstantCompiledExpr {
1082 value: Number(
1083 I64(
1084 3,
1085 ),
1086 ),
1087 },
1088 ),
1089 )
1090 ");
1091
1092 let expr_map = parse_cel("({'a': 1, 'b': 2}).a").unwrap();
1093 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r"
1094 Ok(
1095 Constant(
1096 ConstantCompiledExpr {
1097 value: Number(
1098 I64(
1099 1,
1100 ),
1101 ),
1102 },
1103 ),
1104 )
1105 ");
1106
1107 let expr_map = parse_cel("({'a': 1, 'b': 2})['b']").unwrap();
1108 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r"
1109 Ok(
1110 Constant(
1111 ConstantCompiledExpr {
1112 value: Number(
1113 I64(
1114 2,
1115 ),
1116 ),
1117 },
1118 ),
1119 )
1120 ");
1121 }
1122}