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