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 fn size(input: &std::collections::HashMap<String, bool>) -> Result<u64, ::tinc::__private::cel::CelError<'_>> {
162 Ok(#output)
163 }
164
165 #[test]
166 fn test_contains() {
167 assert_eq!(size(&{
168 let mut map = std::collections::HashMap::new();
169 map.insert("value".to_string(), true);
170 map
171 }).unwrap(), 1);
172 assert_eq!(size(&std::collections::HashMap::new()).unwrap(), 0);
173 assert_eq!(size(&{
174 let mut map = std::collections::HashMap::new();
175 map.insert("xd".to_string(), true);
176 map.insert("value".to_string(), true);
177 map
178 }).unwrap(), 2);
179 }
180 },
181 ));
182 }
183
184 #[test]
185 #[cfg(not(valgrind))]
186 fn test_size_runtime_repeated() {
187 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
188 let compiler = Compiler::new(®istry);
189
190 let string_value = CompiledExpr::runtime(
191 CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Repeated(ProtoValueType::String))),
192 parse_quote!(input),
193 );
194
195 let output = Size
196 .compile(CompilerCtx::new(compiler.child(), Some(string_value), &[]))
197 .unwrap();
198
199 insta::assert_snapshot!(postcompile::compile_str!(
200 postcompile::config! {
201 test: true,
202 dependencies: vec![
203 postcompile::Dependency::version("tinc", "*"),
204 ],
205 },
206 quote::quote! {
207 fn size(input: &Vec<String>) -> Result<u64, ::tinc::__private::cel::CelError<'_>> {
208 Ok(#output)
209 }
210
211 #[test]
212 fn test_contains() {
213 assert_eq!(size(&vec!["value".into()]).unwrap(), 1);
214 assert_eq!(size(&vec![]).unwrap(), 0);
215 assert_eq!(size(&vec!["xd".into(), "value".into()]).unwrap(), 2);
216 }
217 },
218 ));
219 }
220}