tinc_build/codegen/cel/functions/
ends_with.rs

1use syn::parse_quote;
2use tinc_cel::CelValue;
3
4use super::Function;
5use crate::codegen::cel::compiler::{CompileError, CompiledExpr, CompilerCtx, ConstantCompiledExpr};
6use crate::codegen::cel::types::CelType;
7use crate::types::{ProtoType, ProtoValueType};
8
9#[derive(Debug, Clone, Default)]
10pub(crate) struct EndsWith;
11
12// this.endsWith(arg) -> arg in this
13impl Function for EndsWith {
14    fn name(&self) -> &'static str {
15        "endsWith"
16    }
17
18    fn syntax(&self) -> &'static str {
19        "<this>.endsWith(<arg>)"
20    }
21
22    fn compile(&self, ctx: CompilerCtx) -> Result<CompiledExpr, CompileError> {
23        let Some(this) = &ctx.this else {
24            return Err(CompileError::syntax("missing this", self));
25        };
26
27        if ctx.args.len() != 1 {
28            return Err(CompileError::syntax("takes exactly one argument", self));
29        }
30
31        let arg = ctx.resolve(&ctx.args[0])?.into_cel()?;
32        let this = this.clone().into_cel()?;
33
34        match (this, arg) {
35            (
36                CompiledExpr::Constant(ConstantCompiledExpr { value: this }),
37                CompiledExpr::Constant(ConstantCompiledExpr { value: arg }),
38            ) => Ok(CompiledExpr::constant(CelValue::cel_ends_with(this, arg)?)),
39            (this, arg) => Ok(CompiledExpr::runtime(
40                CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
41                parse_quote! {
42                    ::tinc::__private::cel::CelValue::cel_ends_with(
43                        #this,
44                        #arg,
45                    )?
46                },
47            )),
48        }
49    }
50}
51
52#[cfg(test)]
53#[cfg(feature = "prost")]
54#[cfg_attr(coverage_nightly, coverage(off))]
55mod tests {
56    use syn::parse_quote;
57    use tinc_cel::CelValue;
58
59    use crate::codegen::cel::compiler::{CompiledExpr, Compiler, CompilerCtx};
60    use crate::codegen::cel::functions::{EndsWith, Function};
61    use crate::codegen::cel::types::CelType;
62    use crate::extern_paths::ExternPaths;
63    use crate::path_set::PathSet;
64    use crate::types::{ProtoType, ProtoTypeRegistry, ProtoValueType};
65
66    #[test]
67    fn test_ends_with_syntax() {
68        let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
69        let compiler = Compiler::new(&registry);
70        insta::assert_debug_snapshot!(EndsWith.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
71        Err(
72            InvalidSyntax {
73                message: "missing this",
74                syntax: "<this>.endsWith(<arg>)",
75            },
76        )
77        "#);
78
79        insta::assert_debug_snapshot!(EndsWith.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("13.2".into()))), &[])), @r#"
80        Err(
81            InvalidSyntax {
82                message: "takes exactly one argument",
83                syntax: "<this>.endsWith(<arg>)",
84            },
85        )
86        "#);
87
88        insta::assert_debug_snapshot!(EndsWith.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant("some string")), &[
89            cel_parser::parse("'ing'").unwrap(), // not an ident
90        ])), @r"
91        Ok(
92            Constant(
93                ConstantCompiledExpr {
94                    value: Bool(
95                        true,
96                    ),
97                },
98            ),
99        )
100        ");
101    }
102
103    #[test]
104    #[cfg(not(valgrind))]
105    fn test_ends_with_runtime() {
106        let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
107        let compiler = Compiler::new(&registry);
108
109        let string_value =
110            CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::String)), parse_quote!(input));
111
112        let output = EndsWith
113            .compile(CompilerCtx::new(
114                compiler.child(),
115                Some(string_value),
116                &[
117                    cel_parser::parse("'ing'").unwrap(), // not an ident
118                ],
119            ))
120            .unwrap();
121
122        insta::assert_snapshot!(postcompile::compile_str!(
123            postcompile::config! {
124                test: true,
125                dependencies: vec![
126                    postcompile::Dependency::version("tinc", "*"),
127                ],
128            },
129            quote::quote! {
130                fn ends_with(input: &str) -> Result<bool, ::tinc::__private::cel::CelError<'_>> {
131                    Ok(#output)
132                }
133
134                #[test]
135                fn test_to_double() {
136                    assert_eq!(ends_with("testing").unwrap(), true);
137                    assert_eq!(ends_with("smile").unwrap(), false);
138                }
139            },
140        ));
141    }
142}