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 {
421 if !matches!(item, ProtoValueType::Message { .. }) {
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
442 let right = right.into_cel()?;
443
444 match (left, right) {
445 (
446 CompiledExpr::Constant(ConstantCompiledExpr { value: left }),
447 CompiledExpr::Constant(ConstantCompiledExpr { value: right }),
448 ) => match op {
449 RelationOp::LessThan => Ok(CompiledExpr::constant(CelValue::cel_lt(left, right)?)),
450 RelationOp::LessThanEq => Ok(CompiledExpr::constant(CelValue::cel_lte(left, right)?)),
451 RelationOp::GreaterThan => Ok(CompiledExpr::constant(CelValue::cel_gt(left, right)?)),
452 RelationOp::GreaterThanEq => Ok(CompiledExpr::constant(CelValue::cel_gte(left, right)?)),
453 RelationOp::Equals => Ok(CompiledExpr::constant(CelValue::cel_eq(left, right)?)),
454 RelationOp::NotEquals => Ok(CompiledExpr::constant(CelValue::cel_neq(left, right)?)),
455 RelationOp::In => Ok(CompiledExpr::constant(CelValue::cel_in(left, right)?)),
456 },
457 (left, right) => {
458 let op = match op {
459 RelationOp::LessThan => quote! { cel_lt },
460 RelationOp::LessThanEq => quote! { cel_lte },
461 RelationOp::GreaterThan => quote! { cel_gt },
462 RelationOp::GreaterThanEq => quote! { cel_gte },
463 RelationOp::Equals => quote! { cel_eq },
464 RelationOp::NotEquals => quote! { cel_neq },
465 RelationOp::In => quote! { cel_in },
466 };
467
468 Ok(CompiledExpr::runtime(
469 CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
470 parse_quote! {
471 ::tinc::__private::cel::CelValue::#op(
472 #left,
473 #right,
474 )?
475 },
476 ))
477 }
478 }
479}
480
481fn resolve_ternary(
482 ctx: &Compiler,
483 cond: &Expression,
484 left: &Expression,
485 right: &Expression,
486) -> Result<CompiledExpr, CompileError> {
487 let cond = ctx.resolve(cond)?.into_bool(ctx);
488 let left = ctx.resolve(left)?.into_cel()?;
489 let right = ctx.resolve(right)?.into_cel()?;
490
491 match cond {
492 CompiledExpr::Constant(ConstantCompiledExpr { value: cond }) => {
493 if cond.to_bool() {
494 Ok(left)
495 } else {
496 Ok(right)
497 }
498 }
499 cond => Ok(CompiledExpr::runtime(
500 CelType::CelValue,
501 parse_quote! {
502 if (#cond) {
503 #left
504 } else {
505 #right
506 }
507 },
508 )),
509 }
510}
511
512fn resolve_unary(ctx: &Compiler, op: &cel_parser::UnaryOp, expr: &Expression) -> Result<CompiledExpr, CompileError> {
513 let expr = ctx.resolve(expr)?;
514 match op {
515 cel_parser::UnaryOp::Not => {
516 let expr = expr.into_bool(ctx);
517 match expr {
518 CompiledExpr::Constant(ConstantCompiledExpr { value: expr }) => Ok(CompiledExpr::constant(!expr.to_bool())),
519 expr => Ok(CompiledExpr::runtime(
520 CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
521 parse_quote! {
522 !(::tinc::__private::cel::to_bool(#expr))
523 },
524 )),
525 }
526 }
527 cel_parser::UnaryOp::DoubleNot => Ok(expr.into_bool(ctx)),
528 cel_parser::UnaryOp::Minus => {
529 let expr = expr.into_cel()?;
530 match expr {
531 CompiledExpr::Constant(ConstantCompiledExpr { value: expr }) => {
532 Ok(CompiledExpr::constant(CelValue::cel_neg(expr)?))
533 }
534 expr => Ok(CompiledExpr::runtime(
535 CelType::CelValue,
536 parse_quote! {
537 ::tinc::__private::cel::CelValue::cel_neg(#expr)?
538 },
539 )),
540 }
541 }
542 cel_parser::UnaryOp::DoubleMinus => Ok(expr),
543 }
544}
545
546#[cfg(test)]
547#[cfg(feature = "prost")]
548#[cfg_attr(coverage_nightly, coverage(off))]
549mod tests {
550 use cel_parser::parse as parse_cel;
551
552 use super::*;
553 use crate::types::ProtoTypeRegistry;
554
555 #[test]
556 fn test_resolve_atom_int() {
557 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
558 let compiler = Compiler::new(®istry);
559 let expr = parse_cel("1").unwrap();
560 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
561 Ok(
562 Constant(
563 ConstantCompiledExpr {
564 value: Number(
565 I64(
566 1,
567 ),
568 ),
569 },
570 ),
571 )
572 ");
573 }
574
575 #[test]
576 fn test_resolve_atom_uint() {
577 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
578 let compiler = Compiler::new(®istry);
579 let expr = parse_cel("3u").unwrap();
580 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
581 Ok(
582 Constant(
583 ConstantCompiledExpr {
584 value: Number(
585 U64(
586 3,
587 ),
588 ),
589 },
590 ),
591 )
592 ");
593 }
594
595 #[test]
596 fn test_resolve_atom_float() {
597 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
598 let compiler = Compiler::new(®istry);
599 let expr = parse_cel("1.23").unwrap();
600 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
601 Ok(
602 Constant(
603 ConstantCompiledExpr {
604 value: Number(
605 F64(
606 1.23,
607 ),
608 ),
609 },
610 ),
611 )
612 ");
613 }
614
615 #[test]
616 fn test_resolve_atom_string_bytes_bool_null() {
617 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
618 let compiler = Compiler::new(®istry);
619
620 let expr_str = parse_cel("\"foo\"").unwrap();
621 insta::assert_debug_snapshot!(resolve(&compiler, &expr_str), @r#"
622 Ok(
623 Constant(
624 ConstantCompiledExpr {
625 value: String(
626 Owned(
627 "foo",
628 ),
629 ),
630 },
631 ),
632 )
633 "#);
634
635 let expr_bytes = parse_cel("b\"hi\"").unwrap();
636 insta::assert_debug_snapshot!(resolve(&compiler, &expr_bytes), @r#"
637 Ok(
638 Constant(
639 ConstantCompiledExpr {
640 value: Bytes(
641 Owned(
642 b"hi",
643 ),
644 ),
645 },
646 ),
647 )
648 "#);
649
650 let expr_bool = parse_cel("true").unwrap();
651 insta::assert_debug_snapshot!(resolve(&compiler, &expr_bool), @r"
652 Ok(
653 Constant(
654 ConstantCompiledExpr {
655 value: Bool(
656 true,
657 ),
658 },
659 ),
660 )
661 ");
662
663 let expr_null = parse_cel("null").unwrap();
664 insta::assert_debug_snapshot!(resolve(&compiler, &expr_null), @r"
665 Ok(
666 Constant(
667 ConstantCompiledExpr {
668 value: Null,
669 },
670 ),
671 )
672 ");
673 }
674
675 #[test]
676 fn test_resolve_arithmetic_constant() {
677 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
678 let compiler = Compiler::new(®istry);
679
680 let expr = parse_cel("10 + 5").unwrap();
681 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
682 Ok(
683 Constant(
684 ConstantCompiledExpr {
685 value: Number(
686 I64(
687 15,
688 ),
689 ),
690 },
691 ),
692 )
693 ");
694
695 let expr = parse_cel("10 - 4").unwrap();
696 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
697 Ok(
698 Constant(
699 ConstantCompiledExpr {
700 value: Number(
701 I64(
702 6,
703 ),
704 ),
705 },
706 ),
707 )
708 ");
709
710 let expr = parse_cel("6 * 7").unwrap();
711 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
712 Ok(
713 Constant(
714 ConstantCompiledExpr {
715 value: Number(
716 I64(
717 42,
718 ),
719 ),
720 },
721 ),
722 )
723 ");
724
725 let expr = parse_cel("20 / 4").unwrap();
726 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
727 Ok(
728 Constant(
729 ConstantCompiledExpr {
730 value: Number(
731 I64(
732 5,
733 ),
734 ),
735 },
736 ),
737 )
738 ");
739
740 let expr = parse_cel("10 % 3").unwrap();
741 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
742 Ok(
743 Constant(
744 ConstantCompiledExpr {
745 value: Number(
746 I64(
747 1,
748 ),
749 ),
750 },
751 ),
752 )
753 ");
754 }
755
756 #[test]
757 fn test_resolve_relation_constant() {
758 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
759 let compiler = Compiler::new(®istry);
760
761 let expr = parse_cel("1 < 2").unwrap();
762 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
763 Ok(
764 Constant(
765 ConstantCompiledExpr {
766 value: Bool(
767 true,
768 ),
769 },
770 ),
771 )
772 ");
773 let expr = parse_cel("1 <= 1").unwrap();
774 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
775 Ok(
776 Constant(
777 ConstantCompiledExpr {
778 value: Bool(
779 true,
780 ),
781 },
782 ),
783 )
784 ");
785 let expr = parse_cel("2 > 1").unwrap();
786 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
787 Ok(
788 Constant(
789 ConstantCompiledExpr {
790 value: Bool(
791 true,
792 ),
793 },
794 ),
795 )
796 ");
797 let expr = parse_cel("2 >= 2").unwrap();
798 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
799 Ok(
800 Constant(
801 ConstantCompiledExpr {
802 value: Bool(
803 true,
804 ),
805 },
806 ),
807 )
808 ");
809 let expr = parse_cel("1 == 1").unwrap();
810 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
811 Ok(
812 Constant(
813 ConstantCompiledExpr {
814 value: Bool(
815 true,
816 ),
817 },
818 ),
819 )
820 ");
821 let expr = parse_cel("1 != 2").unwrap();
822 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
823 Ok(
824 Constant(
825 ConstantCompiledExpr {
826 value: Bool(
827 true,
828 ),
829 },
830 ),
831 )
832 ");
833 let expr = parse_cel("1 in [1, 2, 3]").unwrap();
834 insta::assert_debug_snapshot!(resolve(&compiler, &expr), @r"
835 Ok(
836 Constant(
837 ConstantCompiledExpr {
838 value: Bool(
839 true,
840 ),
841 },
842 ),
843 )
844 ");
845 }
846
847 #[test]
848 fn test_resolve_boolean_constant() {
849 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
850 let compiler = Compiler::new(®istry);
851
852 let expr_and = parse_cel("true && false").unwrap();
853 insta::assert_debug_snapshot!(resolve(&compiler, &expr_and), @r"
854 Ok(
855 Constant(
856 ConstantCompiledExpr {
857 value: Bool(
858 false,
859 ),
860 },
861 ),
862 )
863 ");
864
865 let expr_or = parse_cel("true || false").unwrap();
866 insta::assert_debug_snapshot!(resolve(&compiler, &expr_or), @r"
867 Ok(
868 Constant(
869 ConstantCompiledExpr {
870 value: Bool(
871 true,
872 ),
873 },
874 ),
875 )
876 ");
877 }
878
879 #[test]
880 fn test_resolve_unary_constant() {
881 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
882 let compiler = Compiler::new(®istry);
883
884 let expr_not = parse_cel("!false").unwrap();
885 insta::assert_debug_snapshot!(resolve(&compiler, &expr_not), @r"
886 Ok(
887 Constant(
888 ConstantCompiledExpr {
889 value: Bool(
890 true,
891 ),
892 },
893 ),
894 )
895 ");
896
897 let expr_double_not = parse_cel("!!true").unwrap();
898 insta::assert_debug_snapshot!(resolve(&compiler, &expr_double_not), @r"
899 Ok(
900 Constant(
901 ConstantCompiledExpr {
902 value: Bool(
903 true,
904 ),
905 },
906 ),
907 )
908 ");
909
910 let expr_neg = parse_cel("-5").unwrap();
911 insta::assert_debug_snapshot!(resolve(&compiler, &expr_neg), @r"
912 Ok(
913 Constant(
914 ConstantCompiledExpr {
915 value: Number(
916 I64(
917 -5,
918 ),
919 ),
920 },
921 ),
922 )
923 ");
924
925 let expr_double_neg = parse_cel("--5").unwrap();
926 insta::assert_debug_snapshot!(resolve(&compiler, &expr_double_neg), @r"
927 Ok(
928 Constant(
929 ConstantCompiledExpr {
930 value: Number(
931 I64(
932 5,
933 ),
934 ),
935 },
936 ),
937 )
938 ");
939 }
940
941 #[test]
942 fn test_resolve_ternary_constant() {
943 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
944 let compiler = Compiler::new(®istry);
945
946 let expr_true = parse_cel("true ? 1 : 2").unwrap();
947 insta::assert_debug_snapshot!(resolve(&compiler, &expr_true), @r"
948 Ok(
949 Constant(
950 ConstantCompiledExpr {
951 value: Number(
952 I64(
953 1,
954 ),
955 ),
956 },
957 ),
958 )
959 ");
960
961 let expr_false = parse_cel("false ? 1 : 2").unwrap();
962 insta::assert_debug_snapshot!(resolve(&compiler, &expr_false), @r"
963 Ok(
964 Constant(
965 ConstantCompiledExpr {
966 value: Number(
967 I64(
968 2,
969 ),
970 ),
971 },
972 ),
973 )
974 ");
975 }
976
977 #[test]
978 fn test_resolve_list_map_constant() {
979 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
980 let compiler = Compiler::new(®istry);
981
982 let expr_list = parse_cel("[1, 2, 3]").unwrap();
983 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
984 Ok(
985 Constant(
986 ConstantCompiledExpr {
987 value: List(
988 [
989 Number(
990 I64(
991 1,
992 ),
993 ),
994 Number(
995 I64(
996 2,
997 ),
998 ),
999 Number(
1000 I64(
1001 3,
1002 ),
1003 ),
1004 ],
1005 ),
1006 },
1007 ),
1008 )
1009 ");
1010
1011 let expr_map = parse_cel("{'a': 1, 'b': 2}").unwrap();
1012 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r#"
1013 Ok(
1014 Constant(
1015 ConstantCompiledExpr {
1016 value: Map(
1017 [
1018 (
1019 String(
1020 Owned(
1021 "a",
1022 ),
1023 ),
1024 Number(
1025 I64(
1026 1,
1027 ),
1028 ),
1029 ),
1030 (
1031 String(
1032 Owned(
1033 "b",
1034 ),
1035 ),
1036 Number(
1037 I64(
1038 2,
1039 ),
1040 ),
1041 ),
1042 ],
1043 ),
1044 },
1045 ),
1046 )
1047 "#);
1048 }
1049
1050 #[test]
1051 fn test_resolve_negative_variable() {
1052 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
1053 let mut compiler = Compiler::new(®istry);
1054
1055 compiler.add_variable("x", CompiledExpr::constant(CelValue::Number(1.into())));
1056
1057 let expr_list = parse_cel("-x").unwrap();
1058 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
1059 Ok(
1060 Constant(
1061 ConstantCompiledExpr {
1062 value: Number(
1063 I64(
1064 -1,
1065 ),
1066 ),
1067 },
1068 ),
1069 )
1070 ");
1071 }
1072
1073 #[test]
1074 fn test_resolve_access() {
1075 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
1076 let compiler = Compiler::new(®istry);
1077
1078 let expr_list = parse_cel("[1, 2, 3][2]").unwrap();
1079 insta::assert_debug_snapshot!(resolve(&compiler, &expr_list), @r"
1080 Ok(
1081 Constant(
1082 ConstantCompiledExpr {
1083 value: Number(
1084 I64(
1085 3,
1086 ),
1087 ),
1088 },
1089 ),
1090 )
1091 ");
1092
1093 let expr_map = parse_cel("({'a': 1, 'b': 2}).a").unwrap();
1094 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r"
1095 Ok(
1096 Constant(
1097 ConstantCompiledExpr {
1098 value: Number(
1099 I64(
1100 1,
1101 ),
1102 ),
1103 },
1104 ),
1105 )
1106 ");
1107
1108 let expr_map = parse_cel("({'a': 1, 'b': 2})['b']").unwrap();
1109 insta::assert_debug_snapshot!(resolve(&compiler, &expr_map), @r"
1110 Ok(
1111 Constant(
1112 ConstantCompiledExpr {
1113 value: Number(
1114 I64(
1115 2,
1116 ),
1117 ),
1118 },
1119 ),
1120 )
1121 ");
1122 }
1123}