tinc_build/codegen/cel/functions/
is_hostname.rs1use 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 IsHostname;
11
12impl Function for IsHostname {
14 fn name(&self) -> &'static str {
15 "isHostname"
16 }
17
18 fn syntax(&self) -> &'static str {
19 "<this>.isHostname()"
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_hostname(value)?))
36 }
37 this => Ok(CompiledExpr::runtime(
38 CelType::Proto(ProtoType::Value(ProtoValueType::Bool)),
39 parse_quote! {{
40 ::tinc::__private::cel::CelValue::cel_is_hostname(
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, IsHostname};
58 use crate::codegen::cel::types::CelType;
59 use crate::extern_paths::ExternPaths;
60 use crate::path_set::PathSet;
61 use crate::types::{ProtoType, ProtoTypeRegistry, ProtoValueType};
62
63 #[test]
64 fn test_is_hostname_syntax() {
65 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
66 let compiler = Compiler::new(®istry);
67 insta::assert_debug_snapshot!(IsHostname.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
68 Err(
69 InvalidSyntax {
70 message: "missing this",
71 syntax: "<this>.isHostname()",
72 },
73 )
74 "#);
75
76 insta::assert_debug_snapshot!(IsHostname.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("192.168.1.1".into()))), &[])), @r"
77 Ok(
78 Constant(
79 ConstantCompiledExpr {
80 value: Bool(
81 false,
82 ),
83 },
84 ),
85 )
86 ");
87
88 insta::assert_debug_snapshot!(IsHostname.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::String("www.scuffle.cloud".into()))), &[])), @r"
89 Ok(
90 Constant(
91 ConstantCompiledExpr {
92 value: Bool(
93 true,
94 ),
95 },
96 ),
97 )
98 ");
99
100 insta::assert_debug_snapshot!(IsHostname.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(CelValue::List(Default::default()))), &[
101 cel_parser::parse("1 + 1").unwrap(), ])), @r#"
103 Err(
104 InvalidSyntax {
105 message: "does not take any arguments",
106 syntax: "<this>.isHostname()",
107 },
108 )
109 "#);
110 }
111
112 #[test]
113 #[cfg(not(valgrind))]
114 fn test_is_hostname_runtime() {
115 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
116 let compiler = Compiler::new(®istry);
117
118 let string_value =
119 CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::String)), parse_quote!(input));
120
121 let output = IsHostname
122 .compile(CompilerCtx::new(compiler.child(), Some(string_value), &[]))
123 .unwrap();
124
125 insta::assert_snapshot!(postcompile::compile_str!(
126 postcompile::config! {
127 test: true,
128 dependencies: vec![
129 postcompile::Dependency::version("tinc", "*"),
130 ],
131 },
132 quote::quote! {
133 fn is_hostname(input: &str) -> Result<bool, ::tinc::__private::cel::CelError<'_>> {
134 Ok(#output)
135 }
136
137 #[test]
138 fn test_is_hostname() {
139 assert_eq!(is_hostname("www.scuffle.cloud").unwrap(), true);
140 assert_eq!(is_hostname("192.168.1.1").unwrap(), false);
141 }
142 },
143 ));
144 }
145}