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::extern_paths::ExternPaths;
64 use crate::path_set::PathSet;
65 use crate::types::{ProtoModifiedValueType, ProtoType, ProtoTypeRegistry, ProtoValueType};
66
67 #[test]
68 fn test_size_syntax() {
69 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
70 let compiler = Compiler::new(®istry);
71 insta::assert_debug_snapshot!(Size.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
72 Err(
73 InvalidSyntax {
74 message: "missing this",
75 syntax: "<this>.size()",
76 },
77 )
78 "#);
79
80 insta::assert_debug_snapshot!(Size.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("13".into()))), &[])), @r"
81 Ok(
82 Constant(
83 ConstantCompiledExpr {
84 value: Number(
85 U64(
86 2,
87 ),
88 ),
89 },
90 ),
91 )
92 ");
93
94 insta::assert_debug_snapshot!(Size.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::List(Default::default()))), &[
95 cel_parser::parse("1 + 1").unwrap(), ])), @r#"
97 Err(
98 InvalidSyntax {
99 message: "takes no arguments",
100 syntax: "<this>.size()",
101 },
102 )
103 "#);
104 }
105
106 #[test]
107 #[cfg(not(valgrind))]
108 fn test_size_runtime() {
109 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
110 let compiler = Compiler::new(®istry);
111
112 let string_value =
113 CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::String)), parse_quote!(input));
114
115 let output = Size
116 .compile(CompilerCtx::new(compiler.child(), Some(string_value), &[]))
117 .unwrap();
118
119 insta::assert_snapshot!(postcompile::compile_str!(
120 postcompile::config! {
121 test: true,
122 dependencies: vec![
123 postcompile::Dependency::version("tinc", "*"),
124 ],
125 },
126 quote::quote! {
127 fn size(input: &str) -> Result<u64, ::tinc::__private::cel::CelError<'_>> {
128 Ok(#output)
129 }
130
131 #[test]
132 fn test_size() {
133 assert_eq!(size("55").unwrap(), 2);
134 }
135 },
136 ));
137 }
138
139 #[test]
140 #[cfg(not(valgrind))]
141 fn test_size_runtime_map() {
142 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
143 let compiler = Compiler::new(®istry);
144
145 let input = CompiledExpr::runtime(
146 CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Map(
147 ProtoValueType::String,
148 ProtoValueType::Bool,
149 ))),
150 parse_quote!(input),
151 );
152
153 let output = Size.compile(CompilerCtx::new(compiler.child(), Some(input), &[])).unwrap();
154
155 insta::assert_snapshot!(postcompile::compile_str!(
156 postcompile::config! {
157 test: true,
158 dependencies: vec![
159 postcompile::Dependency::version("tinc", "*"),
160 ],
161 },
162 quote::quote! {
163 #![allow(unused_parens)]
164
165 fn size(input: &std::collections::HashMap<String, bool>) -> Result<u64, ::tinc::__private::cel::CelError<'_>> {
166 Ok(#output)
167 }
168
169 #[test]
170 fn test_contains() {
171 assert_eq!(size(&{
172 let mut map = std::collections::HashMap::new();
173 map.insert("value".to_string(), true);
174 map
175 }).unwrap(), 1);
176 assert_eq!(size(&std::collections::HashMap::new()).unwrap(), 0);
177 assert_eq!(size(&{
178 let mut map = std::collections::HashMap::new();
179 map.insert("xd".to_string(), true);
180 map.insert("value".to_string(), true);
181 map
182 }).unwrap(), 2);
183 }
184 },
185 ));
186 }
187
188 #[test]
189 #[cfg(not(valgrind))]
190 fn test_size_runtime_repeated() {
191 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
192 let compiler = Compiler::new(®istry);
193
194 let string_value = CompiledExpr::runtime(
195 CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Repeated(ProtoValueType::String))),
196 parse_quote!(input),
197 );
198
199 let output = Size
200 .compile(CompilerCtx::new(compiler.child(), Some(string_value), &[]))
201 .unwrap();
202
203 insta::assert_snapshot!(postcompile::compile_str!(
204 postcompile::config! {
205 test: true,
206 dependencies: vec![
207 postcompile::Dependency::version("tinc", "*"),
208 ],
209 },
210 quote::quote! {
211 #![allow(unused_parens)]
212
213 fn size(input: &Vec<String>) -> Result<u64, ::tinc::__private::cel::CelError<'_>> {
214 Ok(#output)
215 }
216
217 #[test]
218 fn test_contains() {
219 assert_eq!(size(&vec!["value".into()]).unwrap(), 1);
220 assert_eq!(size(&vec![]).unwrap(), 0);
221 assert_eq!(size(&vec!["xd".into(), "value".into()]).unwrap(), 2);
222 }
223 },
224 ));
225 }
226}