tinc_build/codegen/cel/functions/
is_uri.rs

1use syn::parse_quote;
2use tinc_cel::CelValue;
3
4use super::Function;
5use crate::codegen::cel::compiler::{CompileError, CompiledExpr, CompilerCtx, ConstantCompiledExpr};
6use crate::codegen::cel::types::CelType;
7use crate::types::{ProtoType, ProtoValueType};
8
9#[derive(Debug, Clone, Default)]
10pub(crate) struct IsUri;
11
12// this.isUri(arg) -> arg in this
13impl Function for IsUri {
14    fn name(&self) -> &'static str {
15        "isUri"
16    }
17
18    fn syntax(&self) -> &'static str {
19        "<this>.isUri()"
20    }
21
22    fn compile(&self, ctx: CompilerCtx) -> Result<CompiledExpr, CompileError> {
23        let Some(this) = &ctx.this else {
24            return Err(CompileError::syntax("missing this", self));
25        };
26
27        if !ctx.args.is_empty() {
28            return Err(CompileError::syntax("does not take any arguments", self));
29        }
30
31        let this = this.clone().into_cel()?;
32
33        match this {
34            CompiledExpr::Constant(ConstantCompiledExpr { value }) => {
35                Ok(CompiledExpr::constant(CelValue::cel_is_uri(value)?))
36            }
37            this => Ok(CompiledExpr::runtime(
38                CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
39                parse_quote! {{
40                    ::tinc::__private::cel::CelValue::cel_is_uri(
41                        #this,
42                    )?
43                }},
44            )),
45        }
46    }
47}
48
49#[cfg(test)]
50#[cfg(feature = "prost")]
51#[cfg_attr(coverage_nightly, coverage(off))]
52mod tests {
53    use syn::parse_quote;
54    use tinc_cel::CelValue;
55
56    use crate::codegen::cel::compiler::{CompiledExpr, Compiler, CompilerCtx};
57    use crate::codegen::cel::functions::{Function, IsUri};
58    use crate::codegen::cel::types::CelType;
59    use crate::types::{ProtoType, ProtoTypeRegistry, ProtoValueType};
60
61    #[test]
62    fn test_is_uri_syntax() {
63        let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
64        let compiler = Compiler::new(&registry);
65        insta::assert_debug_snapshot!(IsUri.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
66        Err(
67            InvalidSyntax {
68                message: "missing this",
69                syntax: "<this>.isUri()",
70            },
71        )
72        "#);
73
74        insta::assert_debug_snapshot!(IsUri.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("http://google.com".into()))), &[])), @r"
75        Ok(
76            Constant(
77                ConstantCompiledExpr {
78                    value: Bool(
79                        true,
80                    ),
81                },
82            ),
83        )
84        ");
85
86        insta::assert_debug_snapshot!(IsUri.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("https://[fe80::4c1a:18ff:fe0b:7946]".into()))), &[])), @r"
87        Ok(
88            Constant(
89                ConstantCompiledExpr {
90                    value: Bool(
91                        true,
92                    ),
93                },
94            ),
95        )
96        ");
97
98        insta::assert_debug_snapshot!(IsUri.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("not-a-uri".into()))), &[])), @r"
99        Ok(
100            Constant(
101                ConstantCompiledExpr {
102                    value: Bool(
103                        false,
104                    ),
105                },
106            ),
107        )
108        ");
109
110        insta::assert_debug_snapshot!(IsUri.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::List(Default::default()))), &[
111            cel_parser::parse("1 + 1").unwrap(), // not an ident
112        ])), @r#"
113        Err(
114            InvalidSyntax {
115                message: "does not take any arguments",
116                syntax: "<this>.isUri()",
117            },
118        )
119        "#);
120    }
121
122    #[test]
123    #[cfg(not(valgrind))]
124    fn test_is_uri_runtime() {
125        let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
126        let compiler = Compiler::new(&registry);
127
128        let string_value =
129            CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::String)), parse_quote!(input));
130
131        let output = IsUri
132            .compile(CompilerCtx::new(compiler.child(), Some(string_value), &[]))
133            .unwrap();
134
135        insta::assert_snapshot!(postcompile::compile_str!(
136            postcompile::config! {
137                test: true,
138                dependencies: vec![
139                    postcompile::Dependency::version("tinc", "*"),
140                ],
141            },
142            quote::quote! {
143                fn is_uri(input: &str) -> Result<bool, ::tinc::__private::cel::CelError<'_>> {
144                    Ok(#output)
145                }
146
147                #[test]
148                fn test_is_hostname() {
149                    assert_eq!(is_uri("http://google.com").unwrap(), true);
150                    assert_eq!(is_uri("not-a-uri").unwrap(), false);
151                }
152            },
153        ));
154    }
155}