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::types::{ProtoType, ProtoTypeRegistry, ProtoValueType};
63
64    #[test]
65    fn test_ends_with_syntax() {
66        let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
67        let compiler = Compiler::new(&registry);
68        insta::assert_debug_snapshot!(EndsWith.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
69        Err(
70            InvalidSyntax {
71                message: "missing this",
72                syntax: "<this>.endsWith(<arg>)",
73            },
74        )
75        "#);
76
77        insta::assert_debug_snapshot!(EndsWith.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("13.2".into()))), &[])), @r#"
78        Err(
79            InvalidSyntax {
80                message: "takes exactly one argument",
81                syntax: "<this>.endsWith(<arg>)",
82            },
83        )
84        "#);
85
86        insta::assert_debug_snapshot!(EndsWith.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant("some string")), &[
87            cel_parser::parse("'ing'").unwrap(), // not an ident
88        ])), @r"
89        Ok(
90            Constant(
91                ConstantCompiledExpr {
92                    value: Bool(
93                        true,
94                    ),
95                },
96            ),
97        )
98        ");
99    }
100
101    #[test]
102    #[cfg(not(valgrind))]
103    fn test_ends_with_runtime() {
104        let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
105        let compiler = Compiler::new(&registry);
106
107        let string_value =
108            CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::String)), parse_quote!(input));
109
110        let output = EndsWith
111            .compile(CompilerCtx::new(
112                compiler.child(),
113                Some(string_value),
114                &[
115                    cel_parser::parse("'ing'").unwrap(), // not an ident
116                ],
117            ))
118            .unwrap();
119
120        insta::assert_snapshot!(postcompile::compile_str!(
121            postcompile::config! {
122                test: true,
123                dependencies: vec![
124                    postcompile::Dependency::version("tinc", "*"),
125                ],
126            },
127            quote::quote! {
128                fn ends_with(input: &str) -> Result<bool, ::tinc::__private::cel::CelError<'_>> {
129                    Ok(#output)
130                }
131
132                #[test]
133                fn test_to_double() {
134                    assert_eq!(ends_with("testing").unwrap(), true);
135                    assert_eq!(ends_with("smile").unwrap(), false);
136                }
137            },
138        ));
139    }
140}