Skip to content

Commit 3c060a2

Browse files
Switch to dev.cel:cel (#303)
Switch to Google's CEL implementation. ------- Signed-off-by: Sri Krishna <[email protected]> Co-authored-by: Steve Ayers <[email protected]>
1 parent ed6392d commit 3c060a2

24 files changed

+902
-959
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,12 @@ dependencies {
339339
annotationProcessor(libs.nullaway)
340340
api(libs.jspecify)
341341
api(libs.protobuf.java)
342-
implementation(enforcedPlatform(libs.cel))
343-
implementation(libs.cel.core)
342+
implementation(libs.cel)
344343

345344
buf("build.buf:buf:${libs.versions.buf.get()}:${osdetector.classifier}@exe")
346345

347346
testImplementation(libs.assertj)
347+
testImplementation(libs.grpc.protobuf)
348348
testImplementation(platform(libs.junit.bom))
349349
testImplementation("org.junit.jupiter:junit-jupiter")
350350
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

conformance/expected-failures.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
standard_rules/bytes:
2+
- pattern/invalid/not_utf8
3+
# input: [ type.googleapis.com/buf.validate.conformance.cases.BytesPattern ]:{val:"\x99"}
4+
# want: runtime error: value must be valid UTF-8 to apply regexp
5+
# got: validation error (1 violation)
6+
# 1. rule_id: "bytes.pattern"
7+
# message: "value must match regex pattern `^[\\x00-\\x7F]+$`"
8+
# field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_BYTES}
9+
# rule: "bytes.pattern" elements:{field_number:15 field_name:"bytes" field_type:TYPE_MESSAGE} elements:{field_number:4 field_name:"pattern" field_type:TYPE_STRING}

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[versions]
22
assertj = "3.27.3"
33
buf = "1.54.0"
4-
cel = "0.5.3"
4+
cel = "0.9.1"
55
error-prone = "2.38.0"
66
junit = "5.13.0"
77
maven-publish = "0.32.0"
@@ -10,10 +10,10 @@ protobuf = "4.31.1"
1010
[libraries]
1111
assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" }
1212
buf = { module = "build.buf:buf", version.ref = "buf" }
13-
cel = { module = "org.projectnessie.cel:cel-bom", version.ref = "cel" }
14-
cel-core = { module = "org.projectnessie.cel:cel-core" }
13+
cel = { module = "dev.cel:cel", version.ref = "cel" }
1514
errorprone-annotations = { module = "com.google.errorprone:error_prone_annotations", version.ref = "error-prone" }
1615
errorprone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "error-prone" }
16+
grpc-protobuf = { module = "io.grpc:grpc-protobuf", version = "1.73.0" }
1717
jspecify = { module ="org.jspecify:jspecify", version = "1.0.0" }
1818
junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
1919
maven-plugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "maven-publish" }

src/main/java/build/buf/protovalidate/AstExpression.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,46 +15,55 @@
1515
package build.buf.protovalidate;
1616

1717
import build.buf.protovalidate.exceptions.CompilationException;
18-
import com.google.api.expr.v1alpha1.Type;
19-
import org.projectnessie.cel.Ast;
20-
import org.projectnessie.cel.Env;
18+
import dev.cel.common.CelAbstractSyntaxTree;
19+
import dev.cel.common.CelValidationException;
20+
import dev.cel.common.CelValidationResult;
21+
import dev.cel.common.types.CelKind;
22+
import dev.cel.compiler.CelCompiler;
2123

22-
/** {@link AstExpression} is a compiled CEL {@link Ast}. */
24+
/** {@link AstExpression} is a compiled CEL {@link CelAbstractSyntaxTree}. */
2325
class AstExpression {
2426
/** The compiled CEL AST. */
25-
public final Ast ast;
27+
public final CelAbstractSyntaxTree ast;
2628

2729
/** Contains the original expression from the proto file. */
2830
public final Expression source;
2931

3032
/** Constructs a new {@link AstExpression}. */
31-
private AstExpression(Ast ast, Expression source) {
33+
private AstExpression(CelAbstractSyntaxTree ast, Expression source) {
3234
this.ast = ast;
3335
this.source = source;
3436
}
3537

3638
/**
3739
* Compiles the given expression to a {@link AstExpression}.
3840
*
39-
* @param env The CEL environment.
41+
* @param cel The CEL compiler.
4042
* @param expr The expression to compile.
4143
* @return The compiled {@link AstExpression}.
4244
* @throws CompilationException if the expression compilation fails.
4345
*/
44-
public static AstExpression newAstExpression(Env env, Expression expr)
46+
public static AstExpression newAstExpression(CelCompiler cel, Expression expr)
4547
throws CompilationException {
46-
Env.AstIssuesTuple astIssuesTuple = env.compile(expr.expression);
47-
if (astIssuesTuple.hasIssues()) {
48+
CelValidationResult compileResult = cel.compile(expr.expression);
49+
if (!compileResult.getAllIssues().isEmpty()) {
4850
throw new CompilationException(
49-
"Failed to compile expression " + expr.id + ":\n" + astIssuesTuple.getIssues());
51+
"Failed to compile expression " + expr.id + ":\n" + compileResult.getIssueString());
5052
}
51-
Ast ast = astIssuesTuple.getAst();
52-
Type outType = ast.getResultType();
53-
if (outType.getPrimitive() != Type.PrimitiveType.BOOL
54-
&& outType.getPrimitive() != Type.PrimitiveType.STRING) {
53+
CelAbstractSyntaxTree ast;
54+
try {
55+
ast = compileResult.getAst();
56+
} catch (CelValidationException e) {
57+
// This will not happen as we checked for issues, and it only throws when
58+
// it has at least one issue of error severity.
59+
throw new CompilationException(
60+
"Failed to compile expression " + expr.id + ":\n" + compileResult.getIssueString());
61+
}
62+
CelKind outKind = ast.getResultType().kind();
63+
if (outKind != CelKind.BOOL && outKind != CelKind.STRING) {
5564
throw new CompilationException(
5665
String.format(
57-
"Expression outputs, wanted either bool or string: %s %s", expr.id, outType));
66+
"Expression outputs, wanted either bool or string: %s %s", expr.id, outKind));
5867
}
5968
return new AstExpression(ast, expr);
6069
}

src/main/java/build/buf/protovalidate/CelPrograms.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package build.buf.protovalidate;
1616

1717
import build.buf.protovalidate.exceptions.ExecutionException;
18+
import dev.cel.runtime.CelVariableResolver;
1819
import java.util.ArrayList;
1920
import java.util.List;
2021
import org.jspecify.annotations.Nullable;
@@ -44,10 +45,10 @@ public boolean tautology() {
4445
@Override
4546
public List<RuleViolation.Builder> evaluate(Value val, boolean failFast)
4647
throws ExecutionException {
47-
Variable activation = Variable.newThisVariable(val.value(Object.class));
48+
CelVariableResolver bindings = Variable.newThisVariable(val.value(Object.class));
4849
List<RuleViolation.Builder> violations = new ArrayList<>();
4950
for (CompiledProgram program : programs) {
50-
RuleViolation.Builder violation = program.eval(val, activation);
51+
RuleViolation.Builder violation = program.eval(val, bindings);
5152
if (violation != null) {
5253
violations.add(violation);
5354
if (failFast) {

src/main/java/build/buf/protovalidate/CompiledProgram.java

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616

1717
import build.buf.protovalidate.exceptions.ExecutionException;
1818
import build.buf.validate.FieldPath;
19+
import dev.cel.runtime.CelEvaluationException;
20+
import dev.cel.runtime.CelRuntime.Program;
21+
import dev.cel.runtime.CelVariableResolver;
1922
import org.jspecify.annotations.Nullable;
20-
import org.projectnessie.cel.Program;
21-
import org.projectnessie.cel.common.types.Err;
22-
import org.projectnessie.cel.common.types.ref.Val;
2323

2424
/**
2525
* {@link CompiledProgram} is a parsed and type-checked {@link Program} along with the source {@link
@@ -38,6 +38,12 @@ class CompiledProgram {
3838
/** The rule value. */
3939
@Nullable private final Value ruleValue;
4040

41+
/**
42+
* Global variables to pass to the evaluation step. Program/CelRuntime doesn't have a concept of
43+
* global variables.
44+
*/
45+
@Nullable private final CelVariableResolver globals;
46+
4147
/**
4248
* Constructs a new {@link CompiledProgram}.
4349
*
@@ -47,30 +53,38 @@ class CompiledProgram {
4753
* @param ruleValue The rule value.
4854
*/
4955
public CompiledProgram(
50-
Program program, Expression source, @Nullable FieldPath rulePath, @Nullable Value ruleValue) {
56+
Program program,
57+
Expression source,
58+
@Nullable FieldPath rulePath,
59+
@Nullable Value ruleValue,
60+
@Nullable CelVariableResolver globals) {
5161
this.program = program;
5262
this.source = source;
5363
this.rulePath = rulePath;
5464
this.ruleValue = ruleValue;
65+
this.globals = globals;
5566
}
5667

5768
/**
58-
* Evaluate the compiled program with a given set of {@link Variable} bindings.
69+
* Evaluate the compiled program with a given set of {@link Variable} variables.
5970
*
60-
* @param bindings Variable bindings used for the evaluation.
71+
* @param variables Variables used for the evaluation.
6172
* @param fieldValue Field value to return in violations.
6273
* @return The {@link build.buf.validate.Violation} from the evaluation, or null if there are no
6374
* violations.
6475
* @throws ExecutionException If the evaluation of the CEL program fails with an error.
6576
*/
66-
public RuleViolation.@Nullable Builder eval(Value fieldValue, Variable bindings)
77+
public RuleViolation.@Nullable Builder eval(Value fieldValue, CelVariableResolver variables)
6778
throws ExecutionException {
68-
Program.EvalResult evalResult = program.eval(bindings);
69-
Val val = evalResult.getVal();
70-
if (val instanceof Err) {
71-
throw new ExecutionException(String.format("error evaluating %s: %s", source.id, val));
79+
Object value;
80+
try {
81+
if (this.globals != null) {
82+
variables = CelVariableResolver.hierarchicalVariableResolver(variables, this.globals);
83+
}
84+
value = program.eval(variables);
85+
} catch (CelEvaluationException e) {
86+
throw new ExecutionException(String.format("error evaluating %s: %s", source.id, e));
7287
}
73-
Object value = val.value();
7488
if (value instanceof String) {
7589
if ("".equals(value)) {
7690
return null;
@@ -88,7 +102,7 @@ public CompiledProgram(
88102
}
89103
return builder;
90104
} else if (value instanceof Boolean) {
91-
if (val.booleanValue()) {
105+
if (Boolean.TRUE.equals(value)) {
92106
return null;
93107
}
94108
RuleViolation.Builder builder =
@@ -101,7 +115,7 @@ public CompiledProgram(
101115
}
102116
return builder;
103117
} else {
104-
throw new ExecutionException(String.format("resolved to an unexpected type %s", val));
118+
throw new ExecutionException(String.format("resolved to an unexpected type %s", value));
105119
}
106120
}
107121
}

0 commit comments

Comments
 (0)