Skip to content

Commit a97a4e3

Browse files
l46kokcopybara-github
authored andcommitted
Add shorthand type specifier syntax for policy configs
PiperOrigin-RevId: 963769585
1 parent 30f8e6d commit a97a4e3

6 files changed

Lines changed: 752 additions & 14 deletions

File tree

bundle/src/main/java/dev/cel/bundle/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ java_library(
9797
name = "environment",
9898
srcs = [
9999
"CelEnvironment.java",
100+
"TypeSpecifierParser.java",
100101
],
101102
tags = [
102103
],
@@ -111,6 +112,7 @@ java_library(
111112
"//common:container",
112113
"//common:options",
113114
"//common:source",
115+
"//common/formats:parser_context",
114116
"//common/types",
115117
"//common/types:type_providers",
116118
"//compiler:compiler_builder",

bundle/src/main/java/dev/cel/bundle/CelEnvironment.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,19 @@ public static TypeDecl create(String name) {
692692
return newBuilder().setName(name).build();
693693
}
694694

695+
/**
696+
* Parses a type specifier shorthand string (e.g. {@code "list<int>"}, {@code "map<string,
697+
* dyn>"}, {@code "list<~T>"}) into a {@link TypeDecl}.
698+
*/
699+
public static TypeDecl parse(String typeSpecifier) {
700+
return TypeSpecifierParser.parse(typeSpecifier);
701+
}
702+
703+
/** Creates a new {@link TypeDecl} representing a type parameter with the provided name. */
704+
public static TypeDecl ofTypeParam(String typeParamName) {
705+
return newBuilder().setName(typeParamName).setIsTypeParam(true).build();
706+
}
707+
695708
public static TypeDecl.Builder newBuilder() {
696709
return new AutoValue_CelEnvironment_TypeDecl.Builder().setIsTypeParam(false);
697710
}
@@ -747,6 +760,35 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
747760
.orElseThrow(() -> new IllegalArgumentException("Undefined type name: " + name()));
748761
}
749762
}
763+
764+
/**
765+
* Formats this type declaration into its shorthand type specifier string representation (e.g.
766+
* {@code "map<string, int>"}, {@code "list<~T>"}).
767+
*/
768+
public String toSpecifierString() {
769+
StringBuilder sb = new StringBuilder();
770+
formatSpecifier(this, sb);
771+
return sb.toString();
772+
}
773+
774+
private static void formatSpecifier(TypeDecl td, StringBuilder sb) {
775+
if (td.isTypeParam()) {
776+
sb.append('~').append(td.name());
777+
return;
778+
}
779+
sb.append(td.name());
780+
if (td.params().isEmpty()) {
781+
return;
782+
}
783+
sb.append('<');
784+
for (int i = 0; i < td.params().size(); i++) {
785+
if (i > 0) {
786+
sb.append(", ");
787+
}
788+
formatSpecifier(td.params().get(i), sb);
789+
}
790+
sb.append('>');
791+
}
750792
}
751793

752794
/** Represents a feature flag that can be enabled in the environment. */

bundle/src/main/java/dev/cel/bundle/CelEnvironmentYamlParser.java

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222
import static dev.cel.common.formats.YamlHelper.newString;
2323
import static dev.cel.common.formats.YamlHelper.parseYamlSource;
2424
import static dev.cel.common.formats.YamlHelper.validateYamlType;
25-
import static java.util.Collections.singletonList;
2625

2726
import com.google.common.collect.ImmutableList;
2827
import com.google.common.collect.ImmutableSet;
2928
import com.google.errorprone.annotations.CanIgnoreReturnValue;
29+
import com.google.errorprone.annotations.CheckReturnValue;
3030
import dev.cel.bundle.CelEnvironment.Alias;
3131
import dev.cel.bundle.CelEnvironment.ContextVariable;
3232
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
@@ -60,7 +60,7 @@
6060
*/
6161
public final class CelEnvironmentYamlParser {
6262
// Sentinel values to be returned for various declarations when parsing failure is encountered.
63-
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
63+
static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
6464
private static final VariableDecl ERROR_VARIABLE_DECL =
6565
VariableDecl.create(ERROR, ERROR_TYPE_DECL);
6666
private static final FunctionDecl ERROR_FUNCTION_DECL =
@@ -71,9 +71,44 @@ public final class CelEnvironmentYamlParser {
7171
private static final Alias ERROR_ALIAS =
7272
Alias.newBuilder().setAlias(ERROR).setQualifiedName(ERROR).build();
7373

74-
/** Generates a new instance of {@code CelEnvironmentYamlParser}. */
74+
private final boolean enableTypeSpecifiers;
75+
76+
/** Generates a new instance of {@code CelEnvironmentYamlParser} with default options. */
7577
public static CelEnvironmentYamlParser newInstance() {
76-
return new CelEnvironmentYamlParser();
78+
return newBuilder().build();
79+
}
80+
81+
/** Creates a new builder to configure and construct a {@link CelEnvironmentYamlParser}. */
82+
public static Builder newBuilder() {
83+
return new Builder();
84+
}
85+
86+
/** Builder for {@link CelEnvironmentYamlParser}. */
87+
public static final class Builder {
88+
private boolean enableTypeSpecifiers = false;
89+
90+
/**
91+
* Configures the parser to allow for shorthand type specifiers (e.g. {@code "list<int>"},
92+
* {@code "map<string, dyn>"}, {@code "list<~T>"}) in addition to structured mapping
93+
* declarations.
94+
*/
95+
@CanIgnoreReturnValue
96+
public Builder enableTypeSpecifiers(boolean enable) {
97+
this.enableTypeSpecifiers = enable;
98+
return this;
99+
}
100+
101+
/** Builds a new instance of {@link CelEnvironmentYamlParser}. */
102+
@CheckReturnValue
103+
public CelEnvironmentYamlParser build() {
104+
return new CelEnvironmentYamlParser(enableTypeSpecifiers);
105+
}
106+
107+
private Builder() {}
108+
}
109+
110+
private CelEnvironmentYamlParser(boolean enableTypeSpecifiers) {
111+
this.enableTypeSpecifiers = enableTypeSpecifiers;
77112
}
78113

79114
/** Parsers the input {@code environmentYamlSource} and returns a {@link CelEnvironment}. */
@@ -335,6 +370,7 @@ private ContextVariable parseContextVariable(ParserContext<Node> ctx, Node node)
335370
Node valueNode = nodeTuple.getValueNode();
336371
String keyName = ((ScalarNode) keyNode).getValue();
337372
switch (keyName) {
373+
case "type":
338374
case "type_name":
339375
typeName = newString(ctx, valueNode);
340376
break;
@@ -478,7 +514,7 @@ private FunctionDecl parseFunction(ParserContext<Node> ctx, Node node) {
478514
return builder.build();
479515
}
480516

481-
private static ImmutableSet<OverloadDecl> parseOverloads(ParserContext<Node> ctx, Node node) {
517+
private ImmutableSet<OverloadDecl> parseOverloads(ParserContext<Node> ctx, Node node) {
482518
long listId = ctx.collectMetadata(node);
483519
ImmutableSet.Builder<OverloadDecl> overloadSetBuilder = ImmutableSet.builder();
484520
if (!assertYamlType(ctx, listId, node, YamlNodeType.LIST)) {
@@ -553,8 +589,7 @@ private static ImmutableList<String> parseOverloadExamples(ParserContext<Node> c
553589
return builder.build();
554590
}
555591

556-
private static ImmutableList<TypeDecl> parseOverloadArguments(
557-
ParserContext<Node> ctx, Node node) {
592+
private ImmutableList<TypeDecl> parseOverloadArguments(ParserContext<Node> ctx, Node node) {
558593
long listValueId = ctx.collectMetadata(node);
559594
if (!assertYamlType(ctx, listValueId, node, YamlNodeType.LIST)) {
560595
return ImmutableList.of();
@@ -791,7 +826,7 @@ private static ImmutableSet<OverloadSelector> parseFunctionOverloadsSelector(
791826
}
792827

793828
@CanIgnoreReturnValue
794-
private static TypeDecl.Builder parseInlinedTypeDecl(
829+
private TypeDecl.Builder parseInlinedTypeDecl(
795830
ParserContext<Node> ctx, long keyId, Node keyNode, Node valueNode, TypeDecl.Builder builder) {
796831
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
797832
return builder;
@@ -800,24 +835,38 @@ private static TypeDecl.Builder parseInlinedTypeDecl(
800835
// Create a synthetic node to make this behave as if a `type: ` parent node actually exists.
801836
MappingNode mapNode =
802837
new MappingNode(
803-
Tag.MAP, /* value= */ singletonList(new NodeTuple(keyNode, valueNode)), FlowStyle.AUTO);
838+
Tag.MAP,
839+
/* value= */ ImmutableList.of(new NodeTuple(keyNode, valueNode)),
840+
FlowStyle.AUTO);
804841

805842
return parseTypeDeclFields(ctx, mapNode, builder);
806843
}
807844

808-
private static TypeDecl parseTypeDecl(ParserContext<Node> ctx, Node node) {
809-
TypeDecl.Builder builder = TypeDecl.newBuilder();
845+
private TypeDecl parseTypeDecl(ParserContext<Node> ctx, Node node) {
810846
long id = ctx.collectMetadata(node);
847+
if (enableTypeSpecifiers) {
848+
if (validateYamlType(node, YamlNodeType.STRING, YamlNodeType.TEXT)) {
849+
return TypeSpecifierParser.parse(ctx, id, newString(ctx, node));
850+
}
851+
if (validateYamlType(node, YamlNodeType.MAP)) {
852+
TypeDecl.Builder builder = TypeDecl.newBuilder();
853+
return parseTypeDeclFields(ctx, (MappingNode) node, builder).build();
854+
}
855+
assertYamlType(ctx, id, node, YamlNodeType.STRING, YamlNodeType.TEXT, YamlNodeType.MAP);
856+
return ERROR_TYPE_DECL;
857+
}
858+
811859
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
812860
return ERROR_TYPE_DECL;
813861
}
814862

863+
TypeDecl.Builder builder = TypeDecl.newBuilder();
815864
MappingNode mapNode = (MappingNode) node;
816865
return parseTypeDeclFields(ctx, mapNode, builder).build();
817866
}
818867

819868
@CanIgnoreReturnValue
820-
private static TypeDecl.Builder parseTypeDeclFields(
869+
private TypeDecl.Builder parseTypeDeclFields(
821870
ParserContext<Node> ctx, MappingNode mapNode, TypeDecl.Builder builder) {
822871
for (NodeTuple nodeTuple : mapNode.getValue()) {
823872
Node keyNode = nodeTuple.getKeyNode();
@@ -943,6 +992,4 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
943992
return builder;
944993
}
945994
}
946-
947-
private CelEnvironmentYamlParser() {}
948995
}

0 commit comments

Comments
 (0)