Skip to content

Commit 2d09ce8

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
Add support for abbreviations and aliases in container configuration for CEL C++ environment YAML. This allows specifying name, abbreviations, and aliases in a container config instead of just a string. The string syntax is preserved as an alternative
PiperOrigin-RevId: 913934958
1 parent cf31ddf commit 2d09ce8

6 files changed

Lines changed: 289 additions & 11 deletions

File tree

env/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ cc_library(
5252
":config",
5353
"//checker:type_checker_builder",
5454
"//common:constant",
55+
"//common:container",
5556
"//common:decl",
5657
"//common:type",
5758
"//compiler",

env/config.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,16 @@ class Config {
3434

3535
struct ContainerConfig {
3636
std::string name;
37-
// TODO(uncreated-issue/87): add support for aliases and abbreviations.
37+
std::vector<std::string> abbreviations;
38+
struct Alias {
39+
std::string alias;
40+
std::string qualified_name;
41+
};
42+
std::vector<Alias> aliases;
3843

39-
bool IsEmpty() const { return name.empty(); }
44+
bool IsEmpty() const {
45+
return name.empty() && abbreviations.empty() && aliases.empty();
46+
}
4047
};
4148

4249
void SetContainerConfig(ContainerConfig container_config) {

env/env.cc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "absl/strings/string_view.h"
2525
#include "checker/type_checker_builder.h"
2626
#include "common/constant.h"
27+
#include "common/container.h"
2728
#include "common/decl.h"
2829
#include "common/type.h"
2930
#include "compiler/compiler.h"
@@ -130,7 +131,16 @@ absl::StatusOr<std::unique_ptr<CompilerBuilder>> Env::NewCompilerBuilder() {
130131
cel::TypeCheckerBuilder& checker_builder =
131132
compiler_builder->GetCheckerBuilder();
132133

133-
checker_builder.set_container(config_.GetContainerConfig().name);
134+
ExpressionContainer container;
135+
CEL_RETURN_IF_ERROR(
136+
container.SetContainer(config_.GetContainerConfig().name));
137+
for (const auto& abbr : config_.GetContainerConfig().abbreviations) {
138+
CEL_RETURN_IF_ERROR(container.AddAbbreviation(abbr));
139+
}
140+
for (const auto& alias : config_.GetContainerConfig().aliases) {
141+
CEL_RETURN_IF_ERROR(container.AddAlias(alias.alias, alias.qualified_name));
142+
}
143+
checker_builder.SetExpressionContainer(std::move(container));
134144

135145
if (!config_.GetStandardLibraryConfig().disable) {
136146
CEL_RETURN_IF_ERROR(

env/env_test.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,36 @@ TEST(ContainerConfigTest, ContainerConfig) {
314314
EXPECT_THAT(result.GetIssues(), IsEmpty()) << result.FormatError();
315315
}
316316

317+
TEST(ContainerConfigTest, ContainerConfigWithAbbreviations) {
318+
Env env;
319+
env.SetDescriptorPool(internal::GetSharedTestingDescriptorPool());
320+
Config config;
321+
config.SetContainerConfig(
322+
{.name = "cel.expr.conformance",
323+
.abbreviations = {"cel.expr.conformance.proto2.TestAllTypes"}});
324+
env.SetConfig(config);
325+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<Compiler> compiler, env.NewCompiler());
326+
ASSERT_OK_AND_ASSIGN(auto result, compiler->Compile("TestAllTypes{}"));
327+
328+
EXPECT_THAT(result.GetIssues(), IsEmpty()) << result.FormatError();
329+
}
330+
331+
TEST(ContainerConfigTest, ContainerConfigWithAliases) {
332+
Env env;
333+
env.SetDescriptorPool(internal::GetSharedTestingDescriptorPool());
334+
Config config;
335+
config.SetContainerConfig(
336+
{.name = "cel.expr.conformance",
337+
.aliases = {
338+
{.alias = "MyTestType",
339+
.qualified_name = "cel.expr.conformance.proto2.TestAllTypes"}}});
340+
env.SetConfig(config);
341+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<Compiler> compiler, env.NewCompiler());
342+
ASSERT_OK_AND_ASSIGN(auto result, compiler->Compile("MyTestType{}"));
343+
344+
EXPECT_THAT(result.GetIssues(), IsEmpty()) << result.FormatError();
345+
}
346+
317347
struct VariableConfigWithValueTestCase {
318348
Config::VariableConfig variable_config;
319349
std::string validate_type_expr;

env/env_yaml.cc

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,72 @@ absl::Status ParseName(Config& config, absl::string_view yaml,
150150
absl::Status ParseContainerConfig(Config& config, absl::string_view yaml,
151151
const YAML::Node& root) {
152152
const YAML::Node container = root["container"];
153-
if (container.IsDefined()) {
154-
if (!container.IsScalar()) {
155-
return YamlError(yaml, container, "Node 'container' is not a string");
156-
}
153+
if (!container.IsDefined()) {
154+
return absl::OkStatus();
155+
}
156+
157+
if (container.IsScalar()) {
157158
config.SetContainerConfig({.name = GetString(yaml, container)});
159+
return absl::OkStatus();
158160
}
161+
162+
if (!container.IsMap()) {
163+
return YamlError(yaml, container,
164+
"Node 'container' is neither a string nor a map");
165+
}
166+
167+
Config::ContainerConfig container_config;
168+
169+
const YAML::Node name = container["name"];
170+
if (name.IsDefined()) {
171+
if (!name.IsScalar()) {
172+
return YamlError(yaml, name, "Node 'name' in container is not a string");
173+
}
174+
container_config.name = GetString(yaml, name);
175+
}
176+
177+
const YAML::Node abbreviations = container["abbreviations"];
178+
if (abbreviations.IsDefined()) {
179+
if (!abbreviations.IsSequence()) {
180+
return YamlError(yaml, abbreviations,
181+
"Node 'abbreviations' is not a sequence");
182+
}
183+
for (const YAML::Node& abbr : abbreviations) {
184+
if (!abbr.IsScalar()) {
185+
return YamlError(yaml, abbr, "Abbreviation is not a string");
186+
}
187+
container_config.abbreviations.push_back(GetString(yaml, abbr));
188+
}
189+
}
190+
191+
const YAML::Node aliases = container["aliases"];
192+
if (aliases.IsDefined()) {
193+
if (!aliases.IsSequence()) {
194+
return YamlError(yaml, aliases, "Node 'aliases' is not a sequence");
195+
}
196+
for (const YAML::Node& alias_node : aliases) {
197+
if (!alias_node.IsMap()) {
198+
return YamlError(yaml, alias_node, "Alias entry is not a map");
199+
}
200+
const YAML::Node alias_key = alias_node["alias"];
201+
const YAML::Node qualified_name_key = alias_node["qualified_name"];
202+
203+
if (!alias_key.IsDefined() || !alias_key.IsScalar()) {
204+
return YamlError(yaml, alias_node,
205+
"Alias entry missing 'alias' string");
206+
}
207+
if (!qualified_name_key.IsDefined() || !qualified_name_key.IsScalar()) {
208+
return YamlError(yaml, alias_node,
209+
"Alias entry missing 'qualified_name' string");
210+
}
211+
212+
container_config.aliases.push_back(
213+
{.alias = GetString(yaml, alias_key),
214+
.qualified_name = GetString(yaml, qualified_name_key)});
215+
}
216+
}
217+
218+
config.SetContainerConfig(std::move(container_config));
159219
return absl::OkStatus();
160220
}
161221

@@ -686,7 +746,44 @@ void EmitContainerConfig(const Config& env_config, YAML::Emitter& out) {
686746
}
687747

688748
out << YAML::Key << "container";
689-
out << YAML::Value << YAML::DoubleQuoted << container_config.name;
749+
if (container_config.abbreviations.empty() &&
750+
container_config.aliases.empty()) {
751+
out << YAML::Value << YAML::DoubleQuoted << container_config.name;
752+
} else {
753+
out << YAML::Value << YAML::BeginMap;
754+
if (!container_config.name.empty()) {
755+
out << YAML::Key << "name" << YAML::Value << YAML::DoubleQuoted
756+
<< container_config.name;
757+
}
758+
if (!container_config.abbreviations.empty()) {
759+
std::vector<std::string> sorted_abbrs = container_config.abbreviations;
760+
absl::c_sort(sorted_abbrs);
761+
out << YAML::Key << "abbreviations" << YAML::Value << YAML::BeginSeq;
762+
for (const auto& abbr : sorted_abbrs) {
763+
out << YAML::Value << YAML::DoubleQuoted << abbr;
764+
}
765+
out << YAML::EndSeq;
766+
}
767+
if (!container_config.aliases.empty()) {
768+
std::vector<Config::ContainerConfig::Alias> sorted_aliases =
769+
container_config.aliases;
770+
absl::c_sort(sorted_aliases, [](const Config::ContainerConfig::Alias& a,
771+
const Config::ContainerConfig::Alias& b) {
772+
return a.alias < b.alias;
773+
});
774+
out << YAML::Key << "aliases" << YAML::Value << YAML::BeginSeq;
775+
for (const auto& alias : sorted_aliases) {
776+
out << YAML::BeginMap;
777+
out << YAML::Key << "alias" << YAML::Value << YAML::DoubleQuoted
778+
<< alias.alias;
779+
out << YAML::Key << "qualified_name" << YAML::Value
780+
<< YAML::DoubleQuoted << alias.qualified_name;
781+
out << YAML::EndMap;
782+
}
783+
out << YAML::EndSeq;
784+
}
785+
out << YAML::EndMap;
786+
}
690787
}
691788

692789
void EmitExtensionConfigs(const Config& env_config, YAML::Emitter& out) {

env/env_yaml_test.cc

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,31 @@ TEST(EnvYamlTest, ParseContainerConfig) {
5555
Field(&Config::ContainerConfig::name, "test.container"));
5656
}
5757

58+
TEST(EnvYamlTest, ParseContainerConfig_AlternativeSyntax) {
59+
ASSERT_OK_AND_ASSIGN(Config config, EnvConfigFromYaml(R"yaml(
60+
container:
61+
name: test.container
62+
abbreviations:
63+
- abbr1.Abbr1
64+
- abbr2.Abbr2
65+
aliases:
66+
- alias: alias1
67+
qualified_name: qual.name1
68+
- alias: alias2
69+
qualified_name: qual.name2
70+
)yaml"));
71+
72+
const auto& container_config = config.GetContainerConfig();
73+
EXPECT_EQ(container_config.name, "test.container");
74+
EXPECT_THAT(container_config.abbreviations,
75+
UnorderedElementsAre("abbr1.Abbr1", "abbr2.Abbr2"));
76+
ASSERT_THAT(container_config.aliases, SizeIs(2));
77+
EXPECT_EQ(container_config.aliases[0].alias, "alias1");
78+
EXPECT_EQ(container_config.aliases[0].qualified_name, "qual.name1");
79+
EXPECT_EQ(container_config.aliases[1].alias, "alias2");
80+
EXPECT_EQ(container_config.aliases[1].qualified_name, "qual.name2");
81+
}
82+
5883
TEST(EnvYamlTest, ParseExtensionConfigs) {
5984
ASSERT_OK_AND_ASSIGN(Config config, EnvConfigFromYaml(R"yaml(
6085
extensions:
@@ -550,9 +575,78 @@ INSTANTIATE_TEST_SUITE_P(
550575
container:
551576
- error: "error"
552577
)yaml",
553-
.expected_error = "3:19: Node 'container' is not a string\n"
554-
"| - error: \"error\"\n"
555-
"| ^",
578+
.expected_error =
579+
"3:19: Node 'container' is neither a string nor a map\n"
580+
"| - error: \"error\"\n"
581+
"| ^",
582+
},
583+
ParseTestCase{
584+
.yaml = R"yaml(
585+
container:
586+
name: []
587+
)yaml",
588+
.expected_error = "3:25: Node 'name' in container is not a string\n"
589+
"| name: []\n"
590+
"| ^",
591+
},
592+
ParseTestCase{
593+
.yaml = R"yaml(
594+
container:
595+
abbreviations: "abbr"
596+
)yaml",
597+
.expected_error = "3:34: Node 'abbreviations' is not a sequence\n"
598+
"| abbreviations: \"abbr\"\n"
599+
"| ^",
600+
},
601+
ParseTestCase{
602+
.yaml = R"yaml(
603+
container:
604+
abbreviations:
605+
- []
606+
)yaml",
607+
.expected_error = "4:21: Abbreviation is not a string\n"
608+
"| - []\n"
609+
"| ^",
610+
},
611+
ParseTestCase{
612+
.yaml = R"yaml(
613+
container:
614+
aliases: "not a sequence"
615+
)yaml",
616+
.expected_error = "3:28: Node 'aliases' is not a sequence\n"
617+
"| aliases: \"not a sequence\"\n"
618+
"| ^",
619+
},
620+
ParseTestCase{
621+
.yaml = R"yaml(
622+
container:
623+
aliases:
624+
- "not a map"
625+
)yaml",
626+
.expected_error = "4:21: Alias entry is not a map\n"
627+
"| - \"not a map\"\n"
628+
"| ^",
629+
},
630+
ParseTestCase{
631+
.yaml = R"yaml(
632+
container:
633+
aliases:
634+
- qualified_name: "qual"
635+
)yaml",
636+
.expected_error = "4:21: Alias entry missing 'alias' string\n"
637+
"| - qualified_name: \"qual\"\n"
638+
"| ^",
639+
},
640+
ParseTestCase{
641+
.yaml = R"yaml(
642+
container:
643+
aliases:
644+
- alias: "my_alias"
645+
)yaml",
646+
.expected_error = "4:21: Alias entry missing"
647+
" 'qualified_name' string\n"
648+
"| - alias: \"my_alias\"\n"
649+
"| ^",
556650
},
557651
ParseTestCase{
558652
.yaml = R"yaml(
@@ -946,6 +1040,33 @@ std::vector<ExportTestCase> GetExportTestCases() {
9461040
container: "test.container"
9471041
)yaml",
9481042
},
1043+
ExportTestCase{
1044+
.config = []() -> absl::StatusOr<Config> {
1045+
Config config;
1046+
config.SetName("test.env");
1047+
config.SetContainerConfig(
1048+
{.name = "test.container",
1049+
.abbreviations = {"foo", "bar"},
1050+
.aliases = {
1051+
{.alias = "foo", .qualified_name = "test.foo"},
1052+
{.alias = "bar", .qualified_name = "test.bar"},
1053+
}});
1054+
return config;
1055+
}(),
1056+
.expected_yaml = R"yaml(
1057+
name: "test.env"
1058+
container:
1059+
name: "test.container"
1060+
abbreviations:
1061+
- "bar"
1062+
- "foo"
1063+
aliases:
1064+
- alias: "bar"
1065+
qualified_name: "test.bar"
1066+
- alias: "foo"
1067+
qualified_name: "test.foo"
1068+
)yaml",
1069+
},
9491070
ExportTestCase{
9501071
.config = []() -> absl::StatusOr<Config> {
9511072
Config config;
@@ -1385,6 +1506,18 @@ std::vector<std::string> GetRoundTripTestCases() {
13851506
overloads:
13861507
- id: "string_to_timestamp"
13871508
)yaml",
1509+
R"yaml(
1510+
container:
1511+
name: "test.container"
1512+
abbreviations:
1513+
- "abbr1.Abbr1"
1514+
- "abbr2.Abbr2"
1515+
aliases:
1516+
- alias: "alias1"
1517+
qualified_name: "qual.name1"
1518+
- alias: "alias2"
1519+
qualified_name: "qual.name2"
1520+
)yaml",
13881521
R"yaml(
13891522
extensions:
13901523
- name: "bindings"

0 commit comments

Comments
 (0)