use syn::parse_quote;
use tinc_cel::CelValue;
use super::Function;
use crate::codegen::cel::compiler::{CompileError, CompiledExpr, CompilerCtx, ConstantCompiledExpr, RuntimeCompiledExpr};
use crate::codegen::cel::types::CelType;
use crate::types::{ProtoModifiedValueType, ProtoPath, ProtoType, ProtoValueType};
#[derive(Debug, Clone, Default)]
pub(crate) struct Enum(pub Option<ProtoPath>);
impl Function for Enum {
fn name(&self) -> &'static str {
"enum"
}
fn syntax(&self) -> &'static str {
"<this>.enum() | <this>.enum(<path>)"
}
fn compile(&self, ctx: CompilerCtx) -> Result<CompiledExpr, CompileError> {
let Some(this) = ctx.this.as_ref() else {
return Err(CompileError::syntax("missing this", self));
};
if ctx.args.len() > 1 {
return Err(CompileError::syntax("invalid number of arguments", self));
}
let enum_path = if let Some(arg) = ctx.args.first() {
ctx.resolve(arg)?
} else {
match (&this, &self.0) {
(
CompiledExpr::Runtime(RuntimeCompiledExpr {
ty:
CelType::Proto(ProtoType::Modified(ProtoModifiedValueType::Optional(ProtoValueType::Enum(path))))
| CelType::Proto(ProtoType::Value(ProtoValueType::Enum(path))),
..
}),
_,
)
| (_, Some(path)) => CompiledExpr::Constant(ConstantCompiledExpr {
value: CelValue::String(path.0.clone().into()),
}),
_ => {
return Err(CompileError::syntax(
"unable to determine enum type, try providing an explicit path",
self,
));
}
}
};
let this = this.clone().into_cel()?;
let enum_path = enum_path.into_cel()?;
match (this, enum_path) {
(
CompiledExpr::Constant(ConstantCompiledExpr { value: this }),
CompiledExpr::Constant(ConstantCompiledExpr { value: enum_path }),
) => Ok(CompiledExpr::constant(CelValue::cel_to_enum(this, enum_path)?)),
(this, enum_path) => Ok(CompiledExpr::runtime(
CelType::CelValue,
parse_quote! {
::tinc::__private::cel::CelValue::cel_to_enum(
#this,
#enum_path,
)?
},
)),
}
}
}
#[cfg(test)]
#[cfg(feature = "prost")]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use syn::parse_quote;
use crate::codegen::cel::compiler::{CompiledExpr, Compiler, CompilerCtx};
use crate::codegen::cel::functions::{Enum, Function};
use crate::codegen::cel::types::CelType;
use crate::extern_paths::ExternPaths;
use crate::path_set::PathSet;
use crate::types::{ProtoType, ProtoTypeRegistry, ProtoValueType};
#[test]
fn test_enum_syntax() {
let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
let compiler = Compiler::new(®istry);
let enum_ = Enum(None);
insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), None, &[])), @r#"
Err(
InvalidSyntax {
message: "missing this",
syntax: "<this>.enum() | <this>.enum(<path>)",
},
)
"#);
insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(5)), &[])), @r#"
Err(
InvalidSyntax {
message: "unable to determine enum type, try providing an explicit path",
syntax: "<this>.enum() | <this>.enum(<path>)",
},
)
"#);
insta::assert_debug_snapshot!(enum_.compile(CompilerCtx::new(compiler.child(), Some(CompiledExpr::constant(5)), &[
cel_parser::parse("'some.Enum'").unwrap(),
])), @r#"
Ok(
Constant(
ConstantCompiledExpr {
value: Enum(
CelEnum {
tag: Owned(
"some.Enum",
),
value: 5,
},
),
},
),
)
"#);
}
#[test]
#[cfg(not(valgrind))]
fn test_enum_runtime() {
let registry = ProtoTypeRegistry::new(crate::Mode::Prost, ExternPaths::new(crate::Mode::Prost), PathSet::default());
let compiler = Compiler::new(®istry);
let string_value =
CompiledExpr::runtime(CelType::Proto(ProtoType::Value(ProtoValueType::Int32)), parse_quote!(input));
let output = Enum(None)
.compile(CompilerCtx::new(
compiler.child(),
Some(string_value),
&[cel_parser::parse("'some.Enum'").unwrap()],
))
.unwrap();
insta::assert_snapshot!(postcompile::compile_str!(
postcompile::config! {
test: true,
dependencies: vec![
postcompile::Dependency::path("tinc", "../tinc"),
],
},
quote::quote! {
fn to_enum(input: i32) -> Result<::tinc::__private::cel::CelValue<'static>, ::tinc::__private::cel::CelError<'static>> {
Ok(#output)
}
#[test]
fn test_to_enum() {
#[::tinc::reexports::linkme::distributed_slice(::tinc::__private::cel::TINC_CEL_ENUM_VTABLE)]
#[linkme(crate = ::tinc::reexports::linkme)]
static ENUM_VTABLE: ::tinc::__private::cel::EnumVtable = ::tinc::__private::cel::EnumVtable {
proto_path: "some.Enum",
is_valid: |_| {
true
},
to_serde: |_| {
::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed("SERDE"))
},
to_proto: |_| {
::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed("PROTO"))
}
};
::tinc::__private::cel::CelMode::Serde.set();
assert_eq!(to_enum(1).unwrap().to_string(), "SERDE");
::tinc::__private::cel::CelMode::Proto.set();
assert_eq!(to_enum(1).unwrap().to_string(), "PROTO");
}
},
));
}
}