24
24
import dev .cel .common .CelAbstractSyntaxTree ;
25
25
import dev .cel .common .CelMutableAst ;
26
26
import dev .cel .common .CelValidationException ;
27
- import dev .cel .common .ast .CelConstant .Kind ;
28
27
import dev .cel .extensions .CelOptionalLibrary .Function ;
29
28
import dev .cel .optimizer .AstMutator ;
30
29
import dev .cel .optimizer .CelAstOptimizer ;
@@ -75,10 +74,14 @@ private RuleOptimizationResult optimizeRule(Cel cel, CelCompiledRule compiledRul
75
74
long lastOutputId = 0 ;
76
75
for (CelCompiledMatch match : Lists .reverse (compiledRule .matches ())) {
77
76
CelAbstractSyntaxTree conditionAst = match .condition ();
78
- boolean isTriviallyTrue =
79
- conditionAst .getExpr ().constantOrDefault ().getKind ().equals (Kind .BOOLEAN_VALUE )
80
- && conditionAst .getExpr ().constant ().booleanValue ();
77
+ // If the condition is trivially true, none of the matches in the rule causes the result
78
+ // to become optional, and the rule is not the last match, then this will introduce
79
+ // unreachable outputs or rules.
80
+ boolean isTriviallyTrue = match .isConditionLiteral ();
81
+
81
82
switch (match .result ().kind ()) {
83
+ // For the match's output, determine whether the output should be wrapped
84
+ // into an optional value, a conditional, or both.
82
85
case OUTPUT :
83
86
OutputValue matchOutput = match .result ().output ();
84
87
CelMutableAst outAst = CelMutableAst .fromCelAst (matchOutput .ast ());
@@ -107,21 +110,25 @@ private RuleOptimizationResult optimizeRule(Cel cel, CelCompiledRule compiledRul
107
110
lastOutputId = matchOutput .sourceId ();
108
111
continue ;
109
112
case RULE :
113
+ // If the match has a nested rule, then compute the rule and whether it has
114
+ // an optional return value.
110
115
CelCompiledRule matchNestedRule = match .result ().rule ();
111
116
RuleOptimizationResult nestedRule = optimizeRule (cel , matchNestedRule );
117
+ boolean nestedHasOptional = matchNestedRule .hasOptionalOutput ();
112
118
CelMutableAst nestedRuleAst = nestedRule .ast ();
113
- if (isOptionalResult && !nestedRule . isOptionalResult () ) {
119
+ if (isOptionalResult && !nestedHasOptional ) {
114
120
nestedRuleAst =
115
121
astMutator .newGlobalCall (Function .OPTIONAL_OF .getFunction (), nestedRuleAst );
116
122
}
117
- if (!isOptionalResult && nestedRule . isOptionalResult () ) {
123
+ if (!isOptionalResult && nestedHasOptional ) {
118
124
matchAst = astMutator .newGlobalCall (Function .OPTIONAL_OF .getFunction (), matchAst );
119
125
isOptionalResult = true ;
120
126
}
121
- if (!isOptionalResult && !nestedRule .isOptionalResult ()) {
122
- throw new IllegalArgumentException ("Subrule early terminates policy" );
123
- }
124
- if (isTriviallyTrue ) {
127
+ // If either the nested rule or current condition output are optional then
128
+ // use optional.or() to specify the combination of the first and second results
129
+ // Note, the argument order is reversed due to the traversal of matches in
130
+ // reverse order.
131
+ if (isOptionalResult && isTriviallyTrue ) {
125
132
matchAst = astMutator .newMemberCall (nestedRuleAst , Function .OR .getFunction (), matchAst );
126
133
} else {
127
134
matchAst =
@@ -131,6 +138,7 @@ private RuleOptimizationResult optimizeRule(Cel cel, CelCompiledRule compiledRul
131
138
nestedRuleAst ,
132
139
matchAst );
133
140
}
141
+
134
142
assertComposedAstIsValid (
135
143
cel ,
136
144
matchAst ,
0 commit comments