tinc_build/codegen/cel/functions/
ends_with.rs1use 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
12impl 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(®istry);
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(), ])), @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(®istry);
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(), ],
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}