Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ java_library(
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
Expand Down Expand Up @@ -103,6 +102,7 @@ java_library(
":environment",
":environment_exception",
"//common:compiler_common",
"//common:container",
"//common/formats:file_source",
"//common/formats:parser_context",
"//common/formats:yaml_helper",
Expand Down
52 changes: 42 additions & 10 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,9 @@ public abstract class CelEnvironment {
public abstract String name();

/**
* An optional description of the config (example: location of the file containing the config
* content).
* Container, which captures default namespace and aliases for value resolution.
*/
public abstract String container();
public abstract CelContainer container();

/**
* An optional description of the environment (example: location of the file containing the config
Expand Down Expand Up @@ -124,7 +123,12 @@ public abstract static class Builder {

public abstract Builder setDescription(String description);

public abstract Builder setContainer(String container);
public abstract Builder setContainer(CelContainer container);

@CanIgnoreReturnValue
public Builder setContainer(String container) {
return setContainer(CelContainer.ofName(container));
}

@CanIgnoreReturnValue
public Builder addExtensions(ExtensionConfig... extensions) {
Expand Down Expand Up @@ -182,7 +186,7 @@ public static Builder newBuilder() {
return new AutoValue_CelEnvironment.Builder()
.setName("")
.setDescription("")
.setContainer("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
}
Expand All @@ -195,8 +199,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setTypeProvider(celTypeProvider)
.setContainer(CelContainer.ofName(container()))
.addVarDeclarations(
variables().stream()
.map(v -> v.toCelVarDecl(celTypeProvider))
Expand All @@ -206,10 +210,6 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
.map(f -> f.toCelFunctionDecl(celTypeProvider))
.collect(toImmutableList()));

if (!container().isEmpty()) {
compilerBuilder.setContainer(CelContainer.ofName(container()));
}

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);
Expand Down Expand Up @@ -683,6 +683,38 @@ public static ExtensionConfig latest(String name) {
}
}

@AutoValue
abstract static class Alias {
abstract String alias();

abstract String qualifiedName();

static Builder newBuilder() {
return new AutoValue_CelEnvironment_Alias.Builder();
}

@AutoValue.Builder
abstract static class Builder implements RequiredFieldsChecker {

abstract Optional<String> alias();

abstract Optional<String> qualifiedName();

abstract Builder setAlias(String alias);

abstract Builder setQualifiedName(String qualifiedName);

abstract Alias build();

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("alias", this::alias),
RequiredField.of("qualified_name", this::qualifiedName));
}
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((options, version) -> CelExtensions.bindings()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder = CelEnvironment.newBuilder();
envBuilder.setContainer(cel.toCheckerBuilder().container());

// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All @@ -171,8 +174,6 @@ public CelEnvironment export(Cel cel) {

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);

CelEnvironment.Builder envBuilder = CelEnvironment.newBuilder();
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
addStandardLibrarySubsetAndRemoveFromInventory(envBuilder, inventory);
addCustomDecls(envBuilder, inventory);
Expand Down
124 changes: 123 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironmentYamlParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import dev.cel.bundle.CelEnvironment.Alias;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.bundle.CelEnvironment.FunctionDecl;
import dev.cel.bundle.CelEnvironment.LibrarySubset;
Expand All @@ -35,6 +36,7 @@
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.bundle.CelEnvironment.TypeDecl;
import dev.cel.bundle.CelEnvironment.VariableDecl;
import dev.cel.common.CelContainer;
import dev.cel.common.CelIssue;
import dev.cel.common.formats.CelFileSource;
import dev.cel.common.formats.ParserContext;
Expand Down Expand Up @@ -64,6 +66,8 @@ public final class CelEnvironmentYamlParser {
private static final ExtensionConfig ERROR_EXTENSION_DECL = ExtensionConfig.of(ERROR);
private static final FunctionSelector ERROR_FUNCTION_SELECTOR =
FunctionSelector.create(ERROR, ImmutableSet.of());
private static final Alias ERROR_ALIAS =
Alias.newBuilder().setAlias(ERROR).setQualifiedName(ERROR).build();

/** Generates a new instance of {@code CelEnvironmentYamlParser}. */
public static CelEnvironmentYamlParser newInstance() {
Expand All @@ -88,6 +92,124 @@ public CelEnvironment parse(String environmentYamlSource, String description)
return parser.parseYaml(environmentYamlSource, description);
}

private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
// Syntax variant 1: "container: `str`"
if (validateYamlType(node, YamlNodeType.STRING, YamlNodeType.TEXT)) {
return CelContainer.ofName(newString(ctx, node));
}

// Syntax variant 2:
// container
// name: str
// abbreviations:
// - a1
// - a2
// aliases:
// - alias: a1
// qualified_name: q1
// - alias: a2
// qualified_name: q2
if (!assertYamlType(ctx, valueId, node, YamlNodeType.MAP)) {
return CelContainer.ofName(ERROR);
}

CelContainer.Builder builder = CelContainer.newBuilder();
MappingNode variableMap = (MappingNode) node;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
builder.setName(newString(ctx, valueNode));
break;
case "aliases":
ImmutableSet<Alias> aliases = parseAliases(ctx, valueNode);
for (Alias alias : aliases) {
builder.addAlias(alias.alias(), alias.qualifiedName());
}
break;
case "abbreviations":
builder.addAbbreviations(parseAbbreviations(ctx, valueNode));
break;
default:
ctx.reportError(keyId, String.format("Unsupported container tag: %s", keyName));
break;
}
}

return builder.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
if (!assertYamlType(ctx, valueId, node, YamlNodeType.LIST)) {
return aliasSetBuilder.build();
}

SequenceNode variableListNode = (SequenceNode) node;
for (Node elementNode : variableListNode.getValue()) {
aliasSetBuilder.add(parseAlias(ctx, elementNode));
}

return aliasSetBuilder.build();
}

private Alias parseAlias(ParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_ALIAS;
}

Alias.Builder builder = Alias.newBuilder();
MappingNode attrMap = (MappingNode) node;
for (NodeTuple nodeTuple : attrMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "alias":
builder.setAlias(newString(ctx, valueNode));
break;
case "qualified_name":
builder.setQualifiedName(newString(ctx, valueNode));
break;
default:
ctx.reportError(keyId, String.format("Unsupported alias tag: %s", keyName));
break;
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_ALIAS;
}

return builder.build();
}

private ImmutableSet<String> parseAbbreviations(ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!assertYamlType(ctx, valueId, node, YamlNodeType.LIST)) {
return ImmutableSet.of(ERROR);
}

ImmutableSet.Builder<String> builder = ImmutableSet.builder();
SequenceNode nameListNode = (SequenceNode) node;
for (Node elementNode : nameListNode.getValue()) {
long elementId = ctx.collectMetadata(elementNode);
if (!assertYamlType(ctx, elementId, elementNode, YamlNodeType.STRING)) {
return ImmutableSet.of(ERROR);
}

builder.add(((ScalarNode) elementNode).getValue());
}
return builder.build();
}

private ImmutableSet<VariableDecl> parseVariables(ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
ImmutableSet.Builder<VariableDecl> variableSetBuilder = ImmutableSet.builder();
Expand Down Expand Up @@ -620,7 +742,7 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
builder.setDescription(newString(ctx, valueNode));
break;
case "container":
builder.setContainer(newString(ctx, valueNode));
builder.setContainer(parseContainer(ctx, valueNode));
break;
case "variables":
builder.setVariables(parseVariables(ctx, valueNode));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import dev.cel.bundle.CelEnvironment.Alias;
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.LibrarySubset.OverloadSelector;
import dev.cel.common.CelContainer;
import java.util.Comparator;
import java.util.Map;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Node;
Expand Down Expand Up @@ -55,6 +58,8 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.FunctionSelector.class, new RepresentFunctionSelector());
this.multiRepresenters.put(
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
}

public static String toYaml(CelEnvironment environment) {
Expand All @@ -72,7 +77,9 @@ public Node representData(Object data) {
if (!environment.description().isEmpty()) {
configMap.put("description", environment.description());
}
if (!environment.container().isEmpty()) {
if (!environment.container().name().isEmpty()
|| !environment.container().abbreviations().isEmpty()
|| !environment.container().aliases().isEmpty()) {
configMap.put("container", environment.container());
}
if (!environment.extensions().isEmpty()) {
Expand All @@ -91,6 +98,43 @@ public Node representData(Object data) {
}
}

private final class RepresentContainer implements Represent {

@Override
public Node representData(Object data) {
CelContainer container = (CelContainer) data;
ImmutableMap.Builder<String, Object> configMap = ImmutableMap.builder();
if (!container.name().isEmpty()) {
configMap.put("name", container.name());
}
if (!container.abbreviations().isEmpty()) {
configMap.put("abbreviations", container.abbreviations());
}
if (!container.aliases().isEmpty()) {
ImmutableList.Builder<Alias> aliases = ImmutableList.builder();
for (Map.Entry<String, String> entry : container.aliases().entrySet()) {
aliases.add(
Alias.newBuilder()
.setAlias(entry.getKey())
.setQualifiedName(entry.getValue())
.build());
}
configMap.put("aliases", aliases.build());
}
return represent(configMap.buildOrThrow());
}
}

private final class RepresentAlias implements Represent {

@Override
public Node representData(Object data) {
Alias alias = (Alias) data;
return represent(
ImmutableMap.of("alias", alias.alias(), "qualified_name", alias.qualifiedName()));
}
}

private final class RepresentExtensionConfig implements Represent {
@Override
public Node representData(Object data) {
Expand Down
Loading
Loading