tinc_build/codegen/cel/
mod.rs

1use std::collections::HashMap;
2
3use anyhow::Context;
4use compiler::{CompiledExpr, CompilerCtx, ConstantCompiledExpr, RuntimeCompiledExpr};
5use functions::Function;
6use proc_macro2::TokenStream;
7use quote::{format_ident, quote};
8use tinc_cel::CelValue;
9
10pub(crate) mod compiler;
11pub(crate) mod functions;
12pub(crate) mod types;
13
14pub(crate) fn eval_message_fmt(
15    field_full_name: &str,
16    msg: &str,
17    ctx: &compiler::Compiler<'_>,
18) -> anyhow::Result<TokenStream> {
19    let fmt = runtime_format::ParsedFmt::new(msg).map_err(|err| anyhow::anyhow!("failed to parse message format: {err}"))?;
20
21    let mut runtime_args = Vec::new();
22    let mut compile_time_args = HashMap::new();
23
24    // each key itself a cel expression
25    for key in fmt.keys() {
26        let expr = cel_parser::parse(key).context("failed to parse cel expression")?;
27        match functions::String.compile(CompilerCtx::new(ctx.child(), Some(ctx.resolve(&expr)?), &[]))? {
28            CompiledExpr::Constant(ConstantCompiledExpr { value }) => {
29                // we need to escape the '{' & '}'
30                compile_time_args.insert(key, value);
31            }
32            CompiledExpr::Runtime(RuntimeCompiledExpr { expr, .. }) => {
33                let ident = format_ident!("arg_{}", runtime_args.len());
34                compile_time_args.insert(key, CelValue::String(format!("{{{ident}}}").into()));
35                runtime_args.push((key, ident, expr));
36            }
37        }
38    }
39
40    let fmt = fmt.with_args(&compile_time_args).to_string();
41
42    if runtime_args.is_empty() {
43        Ok(quote!(#fmt))
44    } else {
45        let args = runtime_args.iter().map(|(key, ident, expr)| {
46            quote! {
47                #ident = (
48                    || {
49                        ::core::result::Result::Ok::<_, ::tinc::__private::cel::CelError>(#expr)
50                    }
51                )().map_err(|err| {
52                    ::tinc::__private::ValidationError::Expression {
53                        error: err.to_string().into_boxed_str(),
54                        field: #field_full_name,
55                        expression: #key,
56                    }
57                })?
58            }
59        });
60
61        Ok(quote!(format!(#fmt, #(#args),*)))
62    }
63}
64
65#[derive(Debug, Clone, PartialEq)]
66pub(crate) struct CelExpression {
67    pub message: String,
68    pub expression: String,
69    pub jsonschemas: Vec<String>,
70    pub this: Option<CelValue<'static>>,
71}
72
73#[derive(Debug, PartialEq, Clone, Default)]
74pub(crate) struct CelExpressions {
75    pub field: Vec<CelExpression>,
76    pub map_key: Vec<CelExpression>,
77    pub map_value: Vec<CelExpression>,
78    pub repeated_item: Vec<CelExpression>,
79}