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