tinc_build/codegen/cel/functions/
size.rs

1use 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(&registry);
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(), // not an ident
94        ])), @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(&registry);
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(&registry);
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(&registry);
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}