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                #![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(&registry);
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}