tinc_build/codegen/cel/functions/
enum_.rs1use 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, ProtoPath, ProtoType, ProtoValueType};
8
9#[derive(Debug, Clone, Default)]
10pub(crate) struct Enum(pub Option<ProtoPath>);
11
12impl Function for Enum {
13 fn name(&self) -> &'static str {
14 "enum"
15 }
16
17 fn syntax(&self) -> &'static str {
18 "<this>.enum() | <this>.enum(<path>)"
19 }
20
21 fn compile(&self, ctx: CompilerCtx) -> Result<CompiledExpr, CompileError> {
22 let Some(this) = ctx.this.as_ref() else {
23 return Err(CompileError::syntax("missing this", self));
24 };
25
26 if ctx.args.len() > 1 {
27 return Err(CompileError::syntax("invalid number of arguments", self));
28 }
29
30 let enum_path = if let Some(arg) = ctx.args.first() {
31 ctx.resolve(arg)?
32 } else {
33 match (&this, &self.0) {
34 (
35 CompiledExpr::Runtime(RuntimeCompiledExpr {
36 ty:
37 CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Optional(ProtoValueType::Enum(path))))
38 | CelType::Proto(ProtoType::Value(ProtoValueType::Enum(path))),
39 ..
40 }),
41 _,
42 )
43 | (_, Some(path)) => CompiledExpr::Constant(ConstantCompiledExpr {
44 value: CelValue::String(path.0.clone().into()),
45 }),
46 _ => {
47 return Err(CompileError::syntax(
48 "unable to determine enum type, try providing an explicit path",
49 self,
50 ));
51 }
52 }
53 };
54
55 let this = this.clone().into_cel()?;
56 let enum_path = enum_path.into_cel()?;
57
58 match (this, enum_path) {
59 (
60 CompiledExpr::Constant(ConstantCompiledExpr { value: this }),
61 CompiledExpr::Constant(ConstantCompiledExpr { value: enum_path }),
62 ) => Ok(CompiledExpr::constant(CelValue::cel_to_enum(this, enum_path)?)),
63 (this, enum_path) => Ok(CompiledExpr::runtime(
64 CelType::CelValue,
65 parse_quote! {
66 ::tinc::__private::cel::CelValue::cel_to_enum(
67 #this,
68 #enum_path,
69 )?
70 },
71 )),
72 }
73 }
74}
75
76#[cfg(test)]
77#[cfg(feature = "prost")]
78#[cfg_attr(coverage_nightly, coverage(off))]
79mod tests {
80 use syn::parse_quote;
81
82 use crate::codegen::cel::compiler::{CompiledExpr, Compiler, CompilerCtx};
83 use crate::codegen::cel::functions::{Enum, Function};
84 use crate::codegen::cel::types::CelType;
85 use crate::types::{ProtoType, ProtoTypeRegistry, ProtoValueType};
86
87 #[test]
88 fn test_enum_syntax() {
89 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
90 let compiler = Compiler::new(®istry);
91 let enum_ = Enum(None);
92 insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
93 Err(
94 InvalidSyntax {
95 message: "missing this",
96 syntax: "<this>.enum() | <this>.enum(<path>)",
97 },
98 )
99 "#);
100
101 insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(5)), &[])), @r#"
102 Err(
103 InvalidSyntax {
104 message: "unable to determine enum type, try providing an explicit path",
105 syntax: "<this>.enum() | <this>.enum(<path>)",
106 },
107 )
108 "#);
109
110 insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(5)), &[
111 cel_parser::parse("'some.Enum'").unwrap(),
112 ])), @r#"
113 Ok(
114 Constant(
115 ConstantCompiledExpr {
116 value: Enum(
117 CelEnum {
118 tag: Owned(
119 "some.Enum",
120 ),
121 value: 5,
122 },
123 ),
124 },
125 ),
126 )
127 "#);
128 }
129
130 #[test]
131 #[cfg(not(valgrind))]
132 fn test_enum_runtime() {
133 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, crate::extern_paths::ExternPaths::new(crate::Mode::Prost));
134 let compiler = Compiler::new(®istry);
135
136 let string_value =
137 CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::Int32)), parse_quote!(input));
138
139 let output = Enum(None)
140 .compile(CompilerCtx::new(
141 compiler.child(),
142 Some(string_value),
143 &[cel_parser::parse("'some.Enum'").unwrap()],
144 ))
145 .unwrap();
146
147 insta::assert_snapshot!(postcompile::compile_str!(
148 postcompile::config! {
149 test: true,
150 dependencies: vec![
151 postcompile::Dependency::version("tinc", "*"),
152 ],
153 },
154 quote::quote! {
155 fn to_enum(input: i32) -> Result<::tinc::__private::cel::CelValue<'static>, ::tinc::__private::cel::CelError<'static>> {
156 Ok(#output)
157 }
158
159 #[test]
160 fn test_to_enum() {
161 #[::tinc::reexports::linkme::distributed_slice(::tinc::__private::cel::TINC_CEL_ENUM_VTABLE)]
162 #[linkme(crate = ::tinc::reexports::linkme)]
163 static ENUM_VTABLE: ::tinc::__private::cel::EnumVtable = ::tinc::__private::cel::EnumVtable {
164 proto_path: "some.Enum",
165 is_valid: |_| {
166 true
167 },
168 to_serde: |_| {
169 ::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed("SERDE"))
170 },
171 to_proto: |_| {
172 ::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed("PROTO"))
173 }
174 };
175
176 ::tinc::__private::cel::CelMode::Serde.set();
177 assert_eq!(to_enum(1).unwrap().to_string(), "SERDE");
178 ::tinc::__private::cel::CelMode::Proto.set();
179 assert_eq!(to_enum(1).unwrap().to_string(), "PROTO");
180 }
181 },
182 ));
183 }
184}