Skip to content

Commit

Permalink
Add CEL enum support
Browse files Browse the repository at this point in the history
Introduces the `EnumValue`, which behaves like an `IntValue`, but keeps track of the enum name, so that it can be printed using `string.format()`.

PiperOrigin-RevId: 717454914
  • Loading branch information
CEL Dev Team authored and copybara-github committed Jan 21, 2025
1 parent 1fd78a1 commit da9a171
Show file tree
Hide file tree
Showing 17 changed files with 1,910 additions and 34 deletions.
22 changes: 16 additions & 6 deletions bazel/deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ def cel_spec_deps():
urls = ["https://github.com/google/cel-spec/archive/" + CEL_SPEC_GIT_SHA + ".zip"],
)

_ICU4C_VERSION_MAJOR = "76"
_ICU4C_VERSION_MINOR = "1"
_ICU4C_BUILD = """
load("@rules_foreign_cc//foreign_cc:configure.bzl", "configure_make")
Expand All @@ -161,9 +163,9 @@ filegroup(
config_setting(
name = "dbg",
values = {
values = {{
"compilation_mode": "dbg",
},
}},
visibility = ["//visibility:private"],
)
Expand All @@ -178,16 +180,24 @@ configure_make(
"--disable-icuio",
"--disable-layoutex",
"--disable-icu-config",
] + select({
] + select({{
":dbg": ["--enable-debug"],
"//conditions:default": [],
}),
}}),
lib_source = ":all",
out_shared_libs = [
"libicudata.so",
"libicudata.so.{version_major}",
"libicudata.so.{version_major}.{version_minor}",
"libicui18n.so",
"libicui18n.so.{version_major}",
"libicui18n.so.{version_major}.{version_minor}",
"libicutu.so",
"libicutu.so.{version_major}",
"libicutu.so.{version_major}.{version_minor}",
"libicuuc.so",
"libicuuc.so.{version_major}",
"libicuuc.so.{version_major}.{version_minor}",
],
out_static_libs = [
"libicudata.a",
Expand All @@ -198,7 +208,7 @@ configure_make(
args = ["-j 8"],
visibility = ["//visibility:public"],
)
"""
""".format(version_major = _ICU4C_VERSION_MAJOR, version_minor = _ICU4C_VERSION_MINOR)

def cel_cpp_extensions_deps():
http_archive(
Expand All @@ -210,7 +220,7 @@ def cel_cpp_extensions_deps():
http_archive(
name = "icu4c",
sha256 = "dfacb46bfe4747410472ce3e1144bf28a102feeaa4e3875bac9b4c6cf30f4f3e",
url = "https://github.com/unicode-org/icu/releases/download/release-76-1/icu4c-76_1-src.tgz",
url = "https://github.com/unicode-org/icu/releases/download/release-{version_major}-{version_minor}/icu4c-{version_major}_{version_minor}-src.tgz".format(version_major = _ICU4C_VERSION_MAJOR, version_minor = _ICU4C_VERSION_MINOR),
strip_prefix = "icu",
patch_cmds = [
"rm -f source/common/BUILD.bazel",
Expand Down
1 change: 0 additions & 1 deletion common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,6 @@ cc_library(
"//internal:message_equality",
"//internal:number",
"//internal:protobuf_runtime_version",
"//internal:serialize",
"//internal:status_macros",
"//internal:strings",
"//internal:time",
Expand Down
54 changes: 41 additions & 13 deletions common/value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
namespace cel {
namespace {

static constexpr std::array<ValueKind, 25> kValueToKindArray = {
static constexpr std::array kValueToKindArray = {
ValueKind::kError, ValueKind::kBool, ValueKind::kBytes,
ValueKind::kDouble, ValueKind::kDuration, ValueKind::kError,
ValueKind::kInt, ValueKind::kList, ValueKind::kList,
Expand All @@ -72,7 +72,7 @@ static constexpr std::array<ValueKind, 25> kValueToKindArray = {
ValueKind::kNull, ValueKind::kOpaque, ValueKind::kString,
ValueKind::kStruct, ValueKind::kStruct, ValueKind::kStruct,
ValueKind::kTimestamp, ValueKind::kType, ValueKind::kUint,
ValueKind::kUnknown};
ValueKind::kUnknown, ValueKind::kInt};

static_assert(kValueToKindArray.size() ==
absl::variant_size<common_internal::ValueVariant>(),
Expand Down Expand Up @@ -750,17 +750,20 @@ namespace {
Value NonNullEnumValue(
absl::Nonnull<const google::protobuf::EnumValueDescriptor*> value) {
ABSL_DCHECK(value != nullptr);
return IntValue(value->number());
return EnumValue(value);
}

Value NonNullEnumValue(absl::Nonnull<const google::protobuf::EnumDescriptor*> type,
int32_t number) {
ABSL_DCHECK(type != nullptr);
if (type->is_closed()) {
if (ABSL_PREDICT_FALSE(type->FindValueByNumber(number) == nullptr)) {
return ErrorValue(absl::InvalidArgumentError(absl::StrCat(
"closed enum has no such value: ", type->full_name(), ".", number)));
}
const google::protobuf::EnumValueDescriptor* enum_value =
type->FindValueByNumber(number);
if (type->is_closed() && ABSL_PREDICT_FALSE(enum_value == nullptr)) {
return ErrorValue(absl::InvalidArgumentError(absl::StrCat(
"closed enum has no such value: ", type->full_name(), ".", number)));
}
if (enum_value != nullptr) {
return EnumValue(enum_value);
}
return IntValue(number);
}
Expand Down Expand Up @@ -1943,6 +1946,18 @@ absl::optional<IntValue> Value::AsInt() const {
alternative != nullptr) {
return *alternative;
}
if (const auto* alternative = absl::get_if<EnumValue>(&variant_);
alternative != nullptr) {
return IntValue(alternative->NativeValue());
}
return absl::nullopt;
}

absl::optional<EnumValue> Value::AsEnum() const {
if (const auto* alternative = absl::get_if<EnumValue>(&variant_);
alternative != nullptr) {
return *alternative;
}
return absl::nullopt;
}

Expand Down Expand Up @@ -2350,18 +2365,31 @@ ErrorValue Value::GetError() && {
return absl::get<ErrorValue>(std::move(variant_));
}

IntValue Value::GetInt() const {
ABSL_DCHECK(IsInt()) << *this;
return absl::get<IntValue>(variant_);
}

#ifdef ABSL_HAVE_EXCEPTIONS
#define CEL_VALUE_THROW_BAD_VARIANT_ACCESS() throw absl::bad_variant_access()
#else
#define CEL_VALUE_THROW_BAD_VARIANT_ACCESS() \
ABSL_LOG(FATAL) << absl::bad_variant_access().what() /* Crash OK */
#endif

IntValue Value::GetInt() const {
ABSL_DCHECK(IsInt()) << *this;
if (const auto* alternative = absl::get_if<IntValue>(&variant_);
alternative != nullptr) {
return *alternative;
}
if (const auto* alternative = absl::get_if<EnumValue>(&variant_);
alternative != nullptr) {
return IntValue(alternative->NativeValue());
}
CEL_VALUE_THROW_BAD_VARIANT_ACCESS();
}

EnumValue Value::GetEnum() const {
ABSL_DCHECK(IsEnum()) << *this;
return absl::get<EnumValue>(variant_);
}

ListValue Value::GetList() const& {
ABSL_DCHECK(IsList()) << *this;
if (const auto* alternative =
Expand Down
44 changes: 43 additions & 1 deletion common/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,13 @@ class Value final {
bool IsError() const { return absl::holds_alternative<ErrorValue>(variant_); }

// Returns `true` if this value is an instance of an int value.
bool IsInt() const { return absl::holds_alternative<IntValue>(variant_); }
bool IsInt() const {
return absl::holds_alternative<IntValue>(variant_) ||
absl::holds_alternative<EnumValue>(variant_);
}

// Returns `true` if this value is an instance of an enum value.
bool IsEnum() const { return absl::holds_alternative<EnumValue>(variant_); }

// Returns `true` if this value is an instance of a list value.
bool IsList() const {
Expand Down Expand Up @@ -671,6 +677,13 @@ class Value final {
return IsInt();
}

// Convenience method for use with template metaprogramming. See
// `IsEnum()`.
template <typename T>
std::enable_if_t<std::is_same_v<EnumValue, T>, bool> Is() const {
return IsEnum();
}

// Convenience method for use with template metaprogramming. See
// `IsList()`.
template <typename T>
Expand Down Expand Up @@ -856,6 +869,11 @@ class Value final {
// int value. Otherwise an empty optional is returned.
absl::optional<IntValue> AsInt() const;

// Performs a checked cast from a value to an enum value,
// returning a non-empty optional with either a value or reference to the
// enum value. Otherwise an empty optional is returned.
absl::optional<EnumValue> AsEnum() const;

// Performs a checked cast from a value to a list value,
// returning a non-empty optional with either a value or reference to the
// list value. Otherwise an empty optional is returned.
Expand Down Expand Up @@ -1791,6 +1809,11 @@ class Value final {
// false, calling this method is undefined behavior.
IntValue GetInt() const;

// Performs an unchecked cast from a value to an int value. In
// debug builds a best effort is made to crash. If `IsInt()` would return
// false, calling this method is undefined behavior.
EnumValue GetEnum() const;

// Performs an unchecked cast from a value to a list value. In
// debug builds a best effort is made to crash. If `IsList()` would return
// false, calling this method is undefined behavior.
Expand Down Expand Up @@ -2104,6 +2127,25 @@ class Value final {
return GetInt();
}

// Convenience method for use with template metaprogramming. See
// `GetEnum()`.
template <typename T>
std::enable_if_t<std::is_same_v<EnumValue, T>, EnumValue> Get() & {
return GetEnum();
}
template <typename T>
std::enable_if_t<std::is_same_v<EnumValue, T>, EnumValue> Get() const& {
return GetEnum();
}
template <typename T>
std::enable_if_t<std::is_same_v<EnumValue, T>, EnumValue> Get() && {
return GetEnum();
}
template <typename T>
std::enable_if_t<std::is_same_v<EnumValue, T>, EnumValue> Get() const&& {
return GetEnum();
}

// Convenience method for use with template metaprogramming. See
// `GetList()`.
template <typename T>
Expand Down
26 changes: 26 additions & 0 deletions common/values/enum_value.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/protobuf/wrappers.pb.h"
#include "absl/status/statusor.h"
#include "common/value.h"

namespace cel {

absl::StatusOr<Value> EnumValue::Equal(ValueManager& value_manager,
const Value& other) const {
return IntValue(NativeValue()).Equal(value_manager, other);
}

} // namespace cel
Loading

0 comments on commit da9a171

Please sign in to comment.