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