// Copyright 2024 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 "runtime/optional_types.h"
#include
#include
#include
#include
#include
#include "absl/base/nullability.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "base/function_adapter.h"
#include "common/casting.h"
#include "common/type.h"
#include "common/value.h"
#include "internal/casts.h"
#include "internal/number.h"
#include "internal/status_macros.h"
#include "runtime/function_registry.h"
#include "runtime/internal/errors.h"
#include "runtime/internal/runtime_friend_access.h"
#include "runtime/internal/runtime_impl.h"
#include "runtime/runtime_builder.h"
#include "runtime/runtime_options.h"
#include "google/protobuf/arena.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"
namespace cel::extensions {
namespace {
Value OptionalOf(const Value& value, const google::protobuf::DescriptorPool* absl_nonnull,
google::protobuf::MessageFactory* absl_nonnull,
google::protobuf::Arena* absl_nonnull arena) {
return OptionalValue::Of(value, arena);
}
Value OptionalNone() { return OptionalValue::None(); }
Value OptionalOfNonZeroValue(
const Value& value,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
if (value.IsZeroValue()) {
return OptionalNone();
}
return OptionalOf(value, descriptor_pool, message_factory, arena);
}
absl::StatusOr OptionalGetValue(const OpaqueValue& opaque_value) {
if (auto optional_value = opaque_value.AsOptional(); optional_value) {
return optional_value->Value();
}
return ErrorValue{runtime_internal::CreateNoMatchingOverloadError("value")};
}
absl::StatusOr OptionalHasValue(const OpaqueValue& opaque_value) {
if (auto optional_value = opaque_value.AsOptional(); optional_value) {
return BoolValue{optional_value->HasValue()};
}
return ErrorValue{
runtime_internal::CreateNoMatchingOverloadError("hasValue")};
}
absl::StatusOr SelectOptionalFieldStruct(
const StructValue& struct_value, const StringValue& key,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
std::string field_name;
auto field_name_view = key.NativeString(field_name);
CEL_ASSIGN_OR_RETURN(auto has_field,
struct_value.HasFieldByName(field_name_view));
if (!has_field) {
return OptionalValue::None();
}
CEL_ASSIGN_OR_RETURN(
auto field, struct_value.GetFieldByName(field_name_view, descriptor_pool,
message_factory, arena));
return OptionalValue::Of(std::move(field), arena);
}
absl::StatusOr SelectOptionalFieldMap(
const MapValue& map, const StringValue& key,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
absl::optional value;
CEL_ASSIGN_OR_RETURN(value,
map.Find(key, descriptor_pool, message_factory, arena));
if (value) {
return OptionalValue::Of(std::move(*value), arena);
}
return OptionalValue::None();
}
absl::StatusOr SelectOptionalField(
const OpaqueValue& opaque_value, const StringValue& key,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
if (auto optional_value = opaque_value.AsOptional(); optional_value) {
if (!optional_value->HasValue()) {
return OptionalValue::None();
}
auto container = optional_value->Value();
if (auto map_value = container.AsMap(); map_value) {
return SelectOptionalFieldMap(*map_value, key, descriptor_pool,
message_factory, arena);
}
if (auto struct_value = container.AsStruct(); struct_value) {
return SelectOptionalFieldStruct(*struct_value, key, descriptor_pool,
message_factory, arena);
}
}
return ErrorValue{runtime_internal::CreateNoMatchingOverloadError("_[?_]")};
}
absl::StatusOr MapOptIndexOptionalValue(
const MapValue& map, const Value& key,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
absl::optional value;
if (auto double_key = cel::As(key); double_key) {
// Try int/uint.
auto number = internal::Number::FromDouble(double_key->NativeValue());
if (number.LosslessConvertibleToInt()) {
CEL_ASSIGN_OR_RETURN(value,
map.Find(IntValue{number.AsInt()}, descriptor_pool,
message_factory, arena));
if (value) {
return OptionalValue::Of(std::move(*value), arena);
}
}
if (number.LosslessConvertibleToUint()) {
CEL_ASSIGN_OR_RETURN(value,
map.Find(UintValue{number.AsUint()}, descriptor_pool,
message_factory, arena));
if (value) {
return OptionalValue::Of(std::move(*value), arena);
}
}
} else {
CEL_ASSIGN_OR_RETURN(
value, map.Find(key, descriptor_pool, message_factory, arena));
if (value) {
return OptionalValue::Of(std::move(*value), arena);
}
if (auto int_key = key.AsInt(); int_key && int_key->NativeValue() >= 0) {
CEL_ASSIGN_OR_RETURN(
value,
map.Find(UintValue{static_cast(int_key->NativeValue())},
descriptor_pool, message_factory, arena));
if (value) {
return OptionalValue::Of(std::move(*value), arena);
}
} else if (auto uint_key = key.AsUint();
uint_key &&
uint_key->NativeValue() <=
static_cast(std::numeric_limits::max())) {
CEL_ASSIGN_OR_RETURN(
value,
map.Find(IntValue{static_cast(uint_key->NativeValue())},
descriptor_pool, message_factory, arena));
if (value) {
return OptionalValue::Of(std::move(*value), arena);
}
}
}
return OptionalValue::None();
}
absl::StatusOr ListOptIndexOptionalInt(
const ListValue& list, int64_t key,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
CEL_ASSIGN_OR_RETURN(auto list_size, list.Size());
if (key < 0 || static_cast(key) >= list_size) {
return OptionalValue::None();
}
CEL_ASSIGN_OR_RETURN(auto element,
list.Get(static_cast(key), descriptor_pool,
message_factory, arena));
return OptionalValue::Of(std::move(element), arena);
}
absl::StatusOr OptionalOptIndexOptionalValue(
const OpaqueValue& opaque_value, const Value& key,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
if (auto optional_value = As(opaque_value); optional_value) {
if (!optional_value->HasValue()) {
return OptionalValue::None();
}
auto container = optional_value->Value();
if (auto map_value = cel::As(container); map_value) {
return MapOptIndexOptionalValue(*map_value, key, descriptor_pool,
message_factory, arena);
}
if (auto list_value = cel::As(container); list_value) {
if (auto int_value = cel::As(key); int_value) {
return ListOptIndexOptionalInt(*list_value, int_value->NativeValue(),
descriptor_pool, message_factory, arena);
}
}
}
return ErrorValue{runtime_internal::CreateNoMatchingOverloadError("_[?_]")};
}
absl::StatusOr ListFirst(const cel::ListValue& list,
const google::protobuf::DescriptorPool* descriptor_pool,
google::protobuf::MessageFactory* message_factory,
google::protobuf::Arena* arena) {
CEL_ASSIGN_OR_RETURN(size_t size, list.Size());
if (size == 0) {
return Value(OptionalValue::None());
}
CEL_ASSIGN_OR_RETURN(Value value,
list.Get(0, descriptor_pool, message_factory, arena));
return Value(OptionalValue::Of(std::move(value), arena));
}
absl::StatusOr ListLast(const cel::ListValue& list,
const google::protobuf::DescriptorPool* descriptor_pool,
google::protobuf::MessageFactory* message_factory,
google::protobuf::Arena* arena) {
CEL_ASSIGN_OR_RETURN(size_t size, list.Size());
if (size == 0) {
return Value(OptionalValue::None());
}
CEL_ASSIGN_OR_RETURN(Value value,
list.Get(static_cast(size) - 1, descriptor_pool,
message_factory, arena));
return Value(OptionalValue::Of(std::move(value), arena));
}
absl::StatusOr ListUnwrapOpt(
const ListValue& list,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
auto builder = NewListValueBuilder(arena);
CEL_ASSIGN_OR_RETURN(auto list_size, list.Size());
builder->Reserve(list_size);
absl::Status status = list.ForEach(
[&](const Value& value) -> absl::StatusOr {
if (auto optional_value = value.AsOptional(); optional_value) {
if (optional_value->HasValue()) {
CEL_RETURN_IF_ERROR(builder->Add(optional_value->Value()));
}
} else {
return absl::InvalidArgumentError(absl::StrFormat(
"optional.unwrap() expected a list(optional(T)), but %s "
"was found in the list.",
value.GetTypeName()));
}
return true;
},
descriptor_pool, message_factory, arena);
if (!status.ok()) {
return ErrorValue(status);
}
return std::move(*builder).Build();
}
absl::Status RegisterOptionalTypeFunctions(FunctionRegistry& registry,
const RuntimeOptions& options) {
if (!options.enable_qualified_type_identifiers) {
return absl::FailedPreconditionError(
"optional_type requires "
"RuntimeOptions.enable_qualified_type_identifiers");
}
if (!options.enable_heterogeneous_equality) {
return absl::FailedPreconditionError(
"optional_type requires RuntimeOptions.enable_heterogeneous_equality");
}
CEL_RETURN_IF_ERROR(registry.Register(
UnaryFunctionAdapter::CreateDescriptor("optional.of",
false),
UnaryFunctionAdapter::WrapFunction(&OptionalOf)));
CEL_RETURN_IF_ERROR(
registry.Register(UnaryFunctionAdapter::CreateDescriptor(
"optional.ofNonZeroValue", false),
UnaryFunctionAdapter::WrapFunction(
&OptionalOfNonZeroValue)));
CEL_RETURN_IF_ERROR(registry.Register(
NullaryFunctionAdapter::CreateDescriptor("optional.none", false),
NullaryFunctionAdapter::WrapFunction(&OptionalNone)));
CEL_RETURN_IF_ERROR(registry.Register(
UnaryFunctionAdapter<:statusor>,
OpaqueValue>::CreateDescriptor("value", true),
UnaryFunctionAdapter<:statusor>, OpaqueValue>::WrapFunction(
&OptionalGetValue)));
CEL_RETURN_IF_ERROR(registry.Register(
UnaryFunctionAdapter<:statusor>,
OpaqueValue>::CreateDescriptor("hasValue", true),
UnaryFunctionAdapter<:statusor>, OpaqueValue>::WrapFunction(
&OptionalHasValue)));
CEL_RETURN_IF_ERROR(registry.Register(
BinaryFunctionAdapter<:statusor>, StructValue,
StringValue>::CreateDescriptor("_?._", false),
BinaryFunctionAdapter<:statusor>, StructValue, StringValue>::
WrapFunction(&SelectOptionalFieldStruct)));
CEL_RETURN_IF_ERROR(registry.Register(
BinaryFunctionAdapter<:statusor>, MapValue,
StringValue>::CreateDescriptor("_?._", false),
BinaryFunctionAdapter<:statusor>, MapValue, StringValue>::
WrapFunction(&SelectOptionalFieldMap)));
CEL_RETURN_IF_ERROR(registry.Register(
BinaryFunctionAdapter<:statusor>, OpaqueValue,
StringValue>::CreateDescriptor("_?._", false),
BinaryFunctionAdapter<:statusor>, OpaqueValue,
StringValue>::WrapFunction(&SelectOptionalField)));
CEL_RETURN_IF_ERROR(registry.Register(
BinaryFunctionAdapter<:statusor>, MapValue,
Value>::CreateDescriptor("_[?_]", false),
BinaryFunctionAdapter<:statusor>, MapValue,
Value>::WrapFunction(&MapOptIndexOptionalValue)));
CEL_RETURN_IF_ERROR(registry.Register(
BinaryFunctionAdapter<:statusor>, ListValue,
int64_t>::CreateDescriptor("_[?_]", false),
BinaryFunctionAdapter<:statusor>, ListValue,
int64_t>::WrapFunction(&ListOptIndexOptionalInt)));
CEL_RETURN_IF_ERROR(registry.Register(
BinaryFunctionAdapter<:statusor>, OpaqueValue,
Value>::CreateDescriptor("_[?_]", false),
BinaryFunctionAdapter<:statusor>, OpaqueValue, Value>::
WrapFunction(&OptionalOptIndexOptionalValue)));
CEL_RETURN_IF_ERROR(registry.Register(
UnaryFunctionAdapter<:statusor>, ListValue>::CreateDescriptor(
"optional.unwrap", false),
UnaryFunctionAdapter<:statusor>, ListValue>::WrapFunction(
&ListUnwrapOpt)));
CEL_RETURN_IF_ERROR(registry.Register(
UnaryFunctionAdapter<:statusor>, ListValue>::CreateDescriptor(
"unwrapOpt", true),
UnaryFunctionAdapter<:statusor>, ListValue>::WrapFunction(
&ListUnwrapOpt)));
CEL_RETURN_IF_ERROR(registry.Register(
UnaryFunctionAdapter<:statusor>, ListValue>::CreateDescriptor(
"first", true),
UnaryFunctionAdapter<:statusor>, ListValue>::WrapFunction(
&ListFirst)));
CEL_RETURN_IF_ERROR(registry.Register(
UnaryFunctionAdapter<:statusor>, ListValue>::CreateDescriptor(
"last", true),
UnaryFunctionAdapter<:statusor>, ListValue>::WrapFunction(
&ListLast)));
return absl::OkStatus();
}
} // namespace
absl::Status EnableOptionalTypes(RuntimeBuilder& builder) {
auto& runtime = cel::internal::down_cast<:runtimeimpl>(
runtime_internal::RuntimeFriendAccess::GetMutableRuntime(builder));
CEL_RETURN_IF_ERROR(RegisterOptionalTypeFunctions(
builder.function_registry(), runtime.expr_builder().options()));
CEL_RETURN_IF_ERROR(builder.type_registry().RegisterType(OptionalType()));
runtime.expr_builder().enable_optional_types();
return absl::OkStatus();
}
} // namespace cel::extensions