tinc_build/codegen/cel/functions/
size.rs1use syn::parse_quote;
2use tinc_cel::CelValue;
3
4use super::Function;
5use crate::codegen::cel::compiler::{CompileError, CompiledExpr, CompilerCtx, ConstantCompiledExpr, RuntimeCompiledExpr};
6use crate::codegen::cel::types::CelType;
7use crate::types::{ProtoModifiedValueType, ProtoType, ProtoValueType};
8
9#[derive(Debug, Clone, Default)]
10pub(crate) struct Size;
11
12impl Function for Size {
13 fn name(&self) -> &'static str {
14 "size"
15 }
16
17 fn syntax(&self) -> &'static str {
18 "<this>.size()"
19 }
20
21 fn compile(&self, ctx: CompilerCtx) -> Result<CompiledExpr, CompileError> {
22 let Some(this) = ctx.this else {
23 return Err(CompileError::syntax("missing this", self));
24 };
25
26 if !ctx.args.is_empty() {
27 return Err(CompileError::syntax("takes no arguments", self));
28 }
29
30 if let CompiledExpr::Runtime(RuntimeCompiledExpr {
31 expr,
32 ty: CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Repeated(_) | ProtoModifiedValueType::Map(_, _))),
33 }) = &this
34 {
35 return Ok(CompiledExpr::runtime(
36 CelType::Proto(ProtoType::Value(ProtoValueType::UInt64)),
37 parse_quote! {
38 ((#expr).len() as u64)
39 },
40 ));
41 }
42
43 match this.into_cel()? {
44 CompiledExpr::Constant(ConstantCompiledExpr { value }) => Ok(CompiledExpr::constant(CelValue::cel_size(value)?)),
45 CompiledExpr::Runtime(RuntimeCompiledExpr { expr, .. }) => Ok(CompiledExpr::runtime(
46 CelType::Proto(ProtoType::Value(ProtoValueType::UInt64)),
47 parse_quote!(::tinc::__private::cel::CelValue::cel_size(#expr)?),
48 )),
49 }
50 }
51}
52
53#[cfg(test)]
54#[cfg(feature = "prost")]
55#[cfg_attr(coverage_nightly, coverage(off))]
56mod tests {
57 use syn::parse_quote;
58 use tinc_cel::CelValue;
59
60 use crate::codegen::cel::compiler::{CompiledExpr, Compiler, CompilerCtx};
61 use crate::codegen::cel::functions::{Function, Size};
62 use crate::codegen::cel::types::CelType;
63 use crate::types::{ProtoModifiedValueType, ProtoType, ProtoTypeRegistry, ProtoValueType};
64
65 #[test]
66 fn test_size_syntax() {
67 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
68 let compiler = Compiler::new(®istry);
69 insta::assert_debug_snapshot!(Size.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
70 Err(
71 InvalidSyntax {
72 message: "missing this",
73 syntax: "<this>.size()",
74 },
75 )
76 "#);
77
78 insta::assert_debug_snapshot!(Size.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("13".into()))), &[])), @r"
79 Ok(
80 Constant(
81 ConstantCompiledExpr {
82 value: Number(
83 U64(
84 2,
85 ),
86 ),
87 },
88 ),
89 )
90 ");
91
92 insta::assert_debug_snapshot!(Size.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::List(Default::default()))), &[
93 cel_parser::parse("1 + 1").unwrap(), ])), @r#"
95 Err(
96 InvalidSyntax {
97 message: "takes no arguments",
98 syntax: "<this>.size()",
99 },
100 )
101 "#);
102 }
103
104 #[test]
105 #[cfg(not(valgrind))]
106 fn test_size_runtime() {
107 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
108 let compiler = Compiler::new(®istry);
109
110 let string_value =
111 CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::String)), parse_quote!(input));
112
113 let output = Size
114 .compile(CompilerCtx::new(compiler.child(), Some(string_value), &[]))
115 .unwrap();
116
117 insta::assert_snapshot!(postcompile::compile_str!(
118 postcompile::config! {
119 test: true,
120 dependencies: vec![
121 postcompile::Dependency::version("tinc", "*"),
122 ],
123 },
124 quote::quote! {
125 fn size(input: &str) -> Result<u64, ::tinc::__private::cel::CelError<'_>> {
126 Ok(#output)
127 }
128
129 #[test]
130 fn test_size() {
131 assert_eq!(size("55").unwrap(), 2);
132 }
133 },
134 ));
135 }
136
137 #[test]
138 #[cfg(not(valgrind))]
139 fn test_size_runtime_map() {
140 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
141 let compiler = Compiler::new(®istry);
142
143 let input = CompiledExpr::runtime(
144 CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Map(
145 ProtoValueType::String,
146 ProtoValueType::Bool,
147 ))),
148 parse_quote!(input),
149 );
150
151 let output = Size.compile(CompilerCtx::new(compiler.child(), Some(input), &[])).unwrap();
152
153 insta::assert_snapshot!(postcompile::compile_str!(
154 postcompile::config! {
155 test: true,
156 dependencies: vec![
157 postcompile::Dependency::version("tinc", "*"),
158 ],
159 },
160 quote::quote! {
161 #![allow(unused_parens)]
162
163 fn size(input: &std::collections::HashMap<String, bool>) -> Result<u64, ::tinc::__private::cel::CelError<'_>> {
164 Ok(#output)
165 }
166
167 #[test]
168 fn test_contains() {
169 assert_eq!(size(&{
170 let mut map = std::collections::HashMap::new();
171 map.insert("value".to_string(), true);
172 map
173 }).unwrap(), 1);
174 assert_eq!(size(&std::collections::HashMap::new()).unwrap(), 0);
175 assert_eq!(size(&{
176 let mut map = std::collections::HashMap::new();
177 map.insert("xd".to_string(), true);
178 map.insert("value".to_string(), true);
179 map
180 }).unwrap(), 2);
181 }
182 },
183 ));
184 }
185
186 #[test]
187 #[cfg(not(valgrind))]
188 fn test_size_runtime_repeated() {
189 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
190 let compiler = Compiler::new(®istry);
191
192 let string_value = CompiledExpr::runtime(
193 CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Repeated(ProtoValueType::String))),
194 parse_quote!(input),
195 );
196
197 let output = Size
198 .compile(CompilerCtx::new(compiler.child(), Some(string_value), &[]))
199 .unwrap();
200
201 insta::assert_snapshot!(postcompile::compile_str!(
202 postcompile::config! {
203 test: true,
204 dependencies: vec![
205 postcompile::Dependency::version("tinc", "*"),
206 ],
207 },
208 quote::quote! {
209 #![allow(unused_parens)]
210
211 fn size(input: &Vec<String>) -> Result<u64, ::tinc::__private::cel::CelError<'_>> {
212 Ok(#output)
213 }
214
215 #[test]
216 fn test_contains() {
217 assert_eq!(size(&vec!["value".into()]).unwrap(), 1);
218 assert_eq!(size(&vec![]).unwrap(), 0);
219 assert_eq!(size(&vec!["xd".into(), "value".into()]).unwrap(), 2);
220 }
221 },
222 ));
223 }
224}