-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathCompiledProgram.java
More file actions
135 lines (124 loc) · 4.82 KB
/
CompiledProgram.java
File metadata and controls
135 lines (124 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2023-2026 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package build.buf.protovalidate;
import build.buf.protovalidate.exceptions.ExecutionException;
import build.buf.validate.FieldPath;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelRuntime.Program;
import dev.cel.runtime.CelVariableResolver;
import org.jspecify.annotations.Nullable;
/**
* {@link CompiledProgram} is a parsed and type-checked {@link Program} along with the source {@link
* Expression}.
*/
final class CompiledProgram {
/** A compiled CEL program that can be evaluated against a set of variable bindings. */
private final Program program;
/** The original expression that was compiled into the program from the proto file. */
private final Expression source;
/** The field path from FieldRules to the rule value. */
@Nullable private final FieldPath rulePath;
/** The rule value. */
@Nullable private final Value ruleValue;
/**
* Global variables to pass to the evaluation step. Program/CelRuntime doesn't have a concept of
* global variables.
*/
@Nullable private final CelVariableResolver globals;
/** Whether the compiled expression references the {@code now} variable. */
private final boolean usesNow;
/**
* Constructs a new {@link CompiledProgram}.
*
* @param program The compiled CEL program.
* @param source The original expression that was compiled into the program.
* @param rulePath The field path from the FieldRules to the rule value.
* @param ruleValue The rule value.
* @param usesNow Whether the source expression references the {@code now} variable.
*/
CompiledProgram(
Program program,
Expression source,
@Nullable FieldPath rulePath,
@Nullable Value ruleValue,
@Nullable CelVariableResolver globals,
boolean usesNow) {
this.program = program;
this.source = source;
this.rulePath = rulePath;
this.ruleValue = ruleValue;
this.globals = globals;
this.usesNow = usesNow;
}
boolean usesNow() {
return usesNow;
}
/**
* Evaluate the compiled program with a given set of {@link Variable} variables.
*
* @param variables Variables used for the evaluation.
* @param fieldValue Field value to return in violations.
* @return The {@link build.buf.validate.Violation} from the evaluation, or null if there are no
* violations.
* @throws ExecutionException If the evaluation of the CEL program fails with an error.
*/
RuleViolation.@Nullable Builder eval(Value fieldValue, CelVariableResolver variables)
throws ExecutionException {
Object value;
try {
if (this.globals != null) {
variables = CelVariableResolver.hierarchicalVariableResolver(variables, this.globals);
}
value = program.eval(variables);
} catch (CelEvaluationException e) {
throw new ExecutionException(String.format("error evaluating %s: %s", source.id, e));
}
if (value instanceof String) {
if ("".equals(value)) {
return null;
}
RuleViolation.Builder builder =
RuleViolation.newBuilder().setRuleId(this.source.id).setMessage(value.toString());
if (fieldValue.fieldDescriptor() != null) {
builder.setFieldValue(new RuleViolation.FieldValue(fieldValue));
}
if (rulePath != null) {
builder.addAllRulePathElements(rulePath.getElementsList());
}
if (ruleValue != null && ruleValue.fieldDescriptor() != null) {
builder.setRuleValue(new RuleViolation.FieldValue(ruleValue));
}
return builder;
} else if (value instanceof Boolean) {
if (Boolean.TRUE.equals(value)) {
return null;
}
String message = this.source.message;
if (message.isEmpty()) {
message = String.format("\"%s\" returned false", this.source.expression);
}
RuleViolation.Builder builder =
RuleViolation.newBuilder().setRuleId(this.source.id).setMessage(message);
if (rulePath != null) {
builder.addAllRulePathElements(rulePath.getElementsList());
}
if (ruleValue != null && ruleValue.fieldDescriptor() != null) {
builder.setRuleValue(new RuleViolation.FieldValue(ruleValue));
}
return builder;
} else {
throw new ExecutionException(String.format("resolved to an unexpected type %s", value));
}
}
}