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::extern_paths::ExternPaths;
86 use crate::path_set::PathSet;
87 use crate::types::{ProtoType, ProtoTypeRegistry, ProtoValueType};
88
89 #[test]
90 fn test_enum_syntax() {
91 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
92 let compiler = Compiler::new(®istry);
93 let enum_ = Enum(None);
94 insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
95 Err(
96 InvalidSyntax {
97 message: "missing this",
98 syntax: "<this>.enum() | <this>.enum(<path>)",
99 },
100 )
101 "#);
102
103 insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(5)), &[])), @r#"
104 Err(
105 InvalidSyntax {
106 message: "unable to determine enum type, try providing an explicit path",
107 syntax: "<this>.enum() | <this>.enum(<path>)",
108 },
109 )
110 "#);
111
112 insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(5)), &[
113 cel_parser::parse("'some.Enum'").unwrap(),
114 ])), @r#"
115 Ok(
116 Constant(
117 ConstantCompiledExpr {
118 value: Enum(
119 CelEnum {
120 tag: Owned(
121 "some.Enum",
122 ),
123 value: 5,
124 },
125 ),
126 },
127 ),
128 )
129 "#);
130 }
131
132 #[test]
133 #[cfg(not(valgrind))]
134 fn test_enum_runtime() {
135 let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
136 let compiler = Compiler::new(®istry);
137
138 let string_value =
139 CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::Int32)), parse_quote!(input));
140
141 let output = Enum(None)
142 .compile(CompilerCtx::new(
143 compiler.child(),
144 Some(string_value),
145 &[cel_parser::parse("'some.Enum'").unwrap()],
146 ))
147 .unwrap();
148
149 insta::assert_snapshot!(postcompile::compile_str!(
150 postcompile::config! {
151 test: true,
152 dependencies: vec![
153 postcompile::Dependency::version("tinc", "*"),
154 ],
155 },
156 quote::quote! {
157 fn to_enum(input: i32) -> Result<::tinc::__private::cel::CelValue<'static>, ::tinc::__private::cel::CelError<'static>> {
158 Ok(#output)
159 }
160
161 #[test]
162 fn test_to_enum() {
163 #[::tinc::reexports::linkme::distributed_slice(::tinc::__private::cel::TINC_CEL_ENUM_VTABLE)]
164 #[linkme(crate = ::tinc::reexports::linkme)]
165 static ENUM_VTABLE: ::tinc::__private::cel::EnumVtable = ::tinc::__private::cel::EnumVtable {
166 proto_path: "some.Enum",
167 is_valid: |_| {
168 true
169 },
170 to_serde: |_| {
171 ::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed("SERDE"))
172 },
173 to_proto: |_| {
174 ::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed("PROTO"))
175 }
176 };
177
178 ::tinc::__private::cel::CelMode::Serde.set();
179 assert_eq!(to_enum(1).unwrap().to_string(), "SERDE");
180 ::tinc::__private::cel::CelMode::Proto.set();
181 assert_eq!(to_enum(1).unwrap().to_string(), "PROTO");
182 }
183 },
184 ));
185 }
186}