tinc_build/codegen/cel/compiler/
resolve.rs

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(&registry);
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(&registry);
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(&registry);
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(&registry);
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(&registry);
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(&registry);
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(&registry);
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(&registry);
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(&registry);
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(&registry);
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(&registry);
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(&registry);
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}