Skip to content

Commit f9658fc

Browse files
zopieuxcopybara-github
authored andcommitted
Introduce list(<T>).first() and list(<T>).last().
PiperOrigin-RevId: 874634033
1 parent 0928b65 commit f9658fc

4 files changed

Lines changed: 63 additions & 0 deletions

File tree

checker/optional.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class OptionalNames {
8383
static constexpr char kOptionalOrValue[] = "orValue";
8484
static constexpr char kOptionalSelect[] = "_?._";
8585
static constexpr char kOptionalIndex[] = "_[?_]";
86+
static constexpr char kOptionalFirst[] = "first";
87+
static constexpr char kOptionalLast[] = "last";
8688
};
8789

8890
class OptionalOverloads {
@@ -107,6 +109,8 @@ class OptionalOverloads {
107109
"map_optindex_optional_value";
108110
static constexpr char kOptionalMapOptionalIndexValue[] =
109111
"optional_map_optindex_optional_value";
112+
static constexpr char kListFirst[] = "list_first";
113+
static constexpr char kListLast[] = "list_last";
110114
// Syntactic sugar for chained indexing.
111115
static constexpr char kOptionalListIndexInt[] = "optional_list_index_int";
112116
static constexpr char kOptionalMapIndexValue[] = "optional_map_index_value";
@@ -181,6 +185,18 @@ absl::Status RegisterOptionalDecls(TypeCheckerBuilder& builder) {
181185
OptionalOfV(), OptionalMapOfKV(),
182186
TypeParamType("K"))));
183187

188+
CEL_ASSIGN_OR_RETURN(
189+
auto first,
190+
MakeFunctionDecl(OptionalNames::kOptionalFirst,
191+
MakeMemberOverloadDecl(OptionalOverloads::kListFirst,
192+
OptionalOfV(), ListOfV())));
193+
194+
CEL_ASSIGN_OR_RETURN(
195+
auto last,
196+
MakeFunctionDecl(OptionalNames::kOptionalLast,
197+
MakeMemberOverloadDecl(OptionalOverloads::kListLast,
198+
OptionalOfV(), ListOfV())));
199+
184200
CEL_ASSIGN_OR_RETURN(
185201
auto index,
186202
MakeFunctionDecl(
@@ -203,6 +219,8 @@ absl::Status RegisterOptionalDecls(TypeCheckerBuilder& builder) {
203219
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(or_value)));
204220
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(opt_index)));
205221
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(select)));
222+
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(first)));
223+
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(last)));
206224
CEL_RETURN_IF_ERROR(builder.MergeFunction(std::move(index)));
207225

208226
return absl::OkStatus();

checker/optional_test.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ INSTANTIATE_TEST_SUITE_P(
259259
IsOptionalType(TypeSpec(PrimitiveType::kInt64))},
260260
TestCase{"{0: {0: 1}}[?1]['']", _, "no matching overload for '_[_]'"},
261261
TestCase{"{0: {0: 1}}[?1][?'']", _, "no matching overload for '_[?_]'"},
262+
TestCase{"[1, 2, 3].first()",
263+
IsOptionalType(TypeSpec(PrimitiveType::kInt64))},
264+
TestCase{"[1, 2, 3].last()",
265+
IsOptionalType(TypeSpec(PrimitiveType::kInt64))},
262266
TestCase{"optional.of('abc').optMap(x, x + 'def')",
263267
IsOptionalType(TypeSpec(PrimitiveType::kString))},
264268
TestCase{"optional.of('abc').optFlatMap(x, optional.of(x + 'def'))",

runtime/optional_types.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,33 @@ absl::StatusOr<Value> OptionalOptIndexOptionalValue(
229229
return ErrorValue{runtime_internal::CreateNoMatchingOverloadError("_[?_]")};
230230
}
231231

232+
absl::StatusOr<Value> ListFirst(const cel::ListValue& list,
233+
const google::protobuf::DescriptorPool* descriptor_pool,
234+
google::protobuf::MessageFactory* message_factory,
235+
google::protobuf::Arena* arena) {
236+
CEL_ASSIGN_OR_RETURN(size_t size, list.Size());
237+
if (size == 0) {
238+
return Value(OptionalValue::None());
239+
}
240+
CEL_ASSIGN_OR_RETURN(Value value,
241+
list.Get(0, descriptor_pool, message_factory, arena));
242+
return Value(OptionalValue::Of(std::move(value), arena));
243+
}
244+
245+
absl::StatusOr<Value> ListLast(const cel::ListValue& list,
246+
const google::protobuf::DescriptorPool* descriptor_pool,
247+
google::protobuf::MessageFactory* message_factory,
248+
google::protobuf::Arena* arena) {
249+
CEL_ASSIGN_OR_RETURN(size_t size, list.Size());
250+
if (size == 0) {
251+
return Value(OptionalValue::None());
252+
}
253+
CEL_ASSIGN_OR_RETURN(Value value,
254+
list.Get(static_cast<int64_t>(size) - 1, descriptor_pool,
255+
message_factory, arena));
256+
return Value(OptionalValue::Of(std::move(value), arena));
257+
}
258+
232259
absl::StatusOr<Value> ListUnwrapOpt(
233260
const ListValue& list,
234261
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
@@ -332,6 +359,16 @@ absl::Status RegisterOptionalTypeFunctions(FunctionRegistry& registry,
332359
"unwrapOpt", true),
333360
UnaryFunctionAdapter<absl::StatusOr<Value>, ListValue>::WrapFunction(
334361
&ListUnwrapOpt)));
362+
CEL_RETURN_IF_ERROR(registry.Register(
363+
UnaryFunctionAdapter<absl::StatusOr<Value>, ListValue>::CreateDescriptor(
364+
"first", true),
365+
UnaryFunctionAdapter<absl::StatusOr<Value>, ListValue>::WrapFunction(
366+
&ListFirst)));
367+
CEL_RETURN_IF_ERROR(registry.Register(
368+
UnaryFunctionAdapter<absl::StatusOr<Value>, ListValue>::CreateDescriptor(
369+
"last", true),
370+
UnaryFunctionAdapter<absl::StatusOr<Value>, ListValue>::WrapFunction(
371+
&ListLast)));
335372
return absl::OkStatus();
336373
}
337374

runtime/optional_types_test.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,10 @@ INSTANTIATE_TEST_SUITE_P(
296296
{"list_unwrapOpt_no_none",
297297
"[optional.of(42), optional.of(\"a\")].unwrapOpt() == [42, \"a\"]",
298298
BoolValueIs(true)},
299+
{"list_first", "[1, 2, 3].first()", OptionalValueIs(IntValueIs(1))},
300+
{"list_first_empty", "[].first()", OptionalValueIsEmpty()},
301+
{"list_last", "[1, 2, 3].last()", OptionalValueIs(IntValueIs(3))},
302+
{"list_last_empty", "[].last()", OptionalValueIsEmpty()},
299303
}),
300304
/*enable_short_circuiting*/ testing::Bool()));
301305

0 commit comments

Comments
 (0)