Skip to content

Commit 40358a9

Browse files
committed
Improve in-code rule-set composition
This commit adds a `SyntaxElement` abstract class that contains several methods intended for use in constructing rule-sets in code. This class implements two new interfaces (`ToCondition` and `ToExpression`) to support converting between types of syntax elements that compose into rule-sets. The `Condition`, `Parameter`, and `Expression` classes extend this, allowing all types of ruleset syntax to be composed this way.
1 parent 6a05460 commit 40358a9

File tree

26 files changed

+501
-95
lines changed

26 files changed

+501
-95
lines changed

smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/language/functions/AwsPartition.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import software.amazon.smithy.rulesengine.language.evaluation.type.Type;
1919
import software.amazon.smithy.rulesengine.language.evaluation.value.Value;
2020
import software.amazon.smithy.rulesengine.language.syntax.Identifier;
21-
import software.amazon.smithy.rulesengine.language.syntax.expressions.Expression;
21+
import software.amazon.smithy.rulesengine.language.syntax.ToExpression;
2222
import software.amazon.smithy.rulesengine.language.syntax.expressions.ExpressionVisitor;
2323
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.FunctionDefinition;
2424
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.FunctionNode;
@@ -74,7 +74,7 @@ public static Definition getDefinition() {
7474
* @param arg1 the region to retrieve partition information from.
7575
* @return The resulting {@link AwsPartition} function.
7676
*/
77-
public static AwsPartition ofExpressions(Expression arg1) {
77+
public static AwsPartition ofExpressions(ToExpression arg1) {
7878
return DEFINITION.createFunction(FunctionNode.ofExpressions(ID, arg1));
7979
}
8080

smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/language/functions/IsVirtualHostableS3Bucket.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.regex.Pattern;
1111
import software.amazon.smithy.rulesengine.language.evaluation.type.Type;
1212
import software.amazon.smithy.rulesengine.language.evaluation.value.Value;
13+
import software.amazon.smithy.rulesengine.language.syntax.ToExpression;
1314
import software.amazon.smithy.rulesengine.language.syntax.expressions.Expression;
1415
import software.amazon.smithy.rulesengine.language.syntax.expressions.ExpressionVisitor;
1516
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.FunctionDefinition;
@@ -45,10 +46,22 @@ public static Definition getDefinition() {
4546
* @param arg2 whether to allow subdomains.
4647
* @return The resulting {@link IsVirtualHostableS3Bucket} function.
4748
*/
48-
public static IsVirtualHostableS3Bucket ofExpressions(Expression arg1, Expression arg2) {
49+
public static IsVirtualHostableS3Bucket ofExpressions(ToExpression arg1, ToExpression arg2) {
4950
return DEFINITION.createFunction(FunctionNode.ofExpressions(ID, arg1, arg2));
5051
}
5152

53+
54+
/**
55+
* Creates a {@link IsVirtualHostableS3Bucket} function from the given expressions.
56+
*
57+
* @param arg1 the value to check.
58+
* @param arg2 whether to allow subdomains.
59+
* @return The resulting {@link IsVirtualHostableS3Bucket} function.
60+
*/
61+
public static IsVirtualHostableS3Bucket ofExpressions(ToExpression arg1, boolean arg2) {
62+
return ofExpressions(arg1, Expression.of(arg2));
63+
}
64+
5265
@Override
5366
public <T> T accept(ExpressionVisitor<T> visitor) {
5467
return visitor.visitLibraryFunction(DEFINITION, getArguments());

smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/language/functions/ParseArn.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import software.amazon.smithy.rulesengine.language.evaluation.type.Type;
1515
import software.amazon.smithy.rulesengine.language.evaluation.value.Value;
1616
import software.amazon.smithy.rulesengine.language.syntax.Identifier;
17-
import software.amazon.smithy.rulesengine.language.syntax.expressions.Expression;
17+
import software.amazon.smithy.rulesengine.language.syntax.ToExpression;
1818
import software.amazon.smithy.rulesengine.language.syntax.expressions.ExpressionVisitor;
1919
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.FunctionDefinition;
2020
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.FunctionNode;
@@ -56,7 +56,7 @@ public static Definition getDefinition() {
5656
* @param arg1 the ARN to parse.
5757
* @return The resulting {@link ParseArn} function.
5858
*/
59-
public static ParseArn ofExpressions(Expression arg1) {
59+
public static ParseArn ofExpressions(ToExpression arg1) {
6060
return DEFINITION.createFunction(FunctionNode.ofExpressions(ID, arg1));
6161
}
6262

smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/language/functions/FunctionsTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ public void awsPartitionOfExpression() {
1616
@Test
1717
public void isVirtualHostableS3BucketOfExpression() {
1818
IsVirtualHostableS3Bucket function = IsVirtualHostableS3Bucket.ofExpressions(
19-
Expression.of("foobar"), Expression.of(true));
19+
Expression.of("foobar"), true);
2020
assertThat(function, instanceOf(IsVirtualHostableS3Bucket.class));
21+
22+
IsVirtualHostableS3Bucket function2 = IsVirtualHostableS3Bucket.ofExpressions(
23+
Expression.of("foobar"), Expression.of(true));
24+
assertThat(function2, instanceOf(IsVirtualHostableS3Bucket.class));
2125
}
2226

2327
@Test
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.rulesengine.language.syntax;
7+
8+
import software.amazon.smithy.rulesengine.language.syntax.expressions.Expression;
9+
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.BooleanEquals;
10+
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.GetAttr;
11+
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.IsSet;
12+
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.IsValidHostLabel;
13+
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.Not;
14+
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.ParseUrl;
15+
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.StringEquals;
16+
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.Substring;
17+
import software.amazon.smithy.rulesengine.language.syntax.rule.Condition;
18+
import software.amazon.smithy.utils.SmithyInternalApi;
19+
20+
/**
21+
* A class that is coercible into {@link Condition}s and {@link Expression}s for
22+
* use in composing rule-sets in code.
23+
*/
24+
@SmithyInternalApi
25+
public abstract class SyntaxElement implements ToCondition, ToExpression {
26+
/**
27+
* Returns a BooleanEquals expression comparing this expression to the provided boolean value.
28+
*
29+
* @param value the value to compare against.
30+
* @return the {@link BooleanEquals} function.
31+
*/
32+
public final BooleanEquals booleanEqual(boolean value) {
33+
return BooleanEquals.ofExpressions(toExpression(), Expression.of(value));
34+
}
35+
36+
/**
37+
* Returns a StringEquals function of this expression and the given string value.
38+
*
39+
* @param value the string value to compare this expression to.
40+
* @return the {@link StringEquals} function.
41+
*/
42+
public final StringEquals stringEqual(String value) {
43+
return StringEquals.ofExpressions(toExpression(), Expression.of(value));
44+
}
45+
46+
/**
47+
* Returns a GetAttr function containing the given path string.
48+
*
49+
* @param path the path.
50+
* @return the {@link GetAttr} function.
51+
*/
52+
public final GetAttr getAttr(String path) {
53+
return GetAttr.ofExpressions(toExpression(), path);
54+
}
55+
56+
/**
57+
* Returns a GetAttr function containing the given identifier.
58+
*
59+
* @param path the path.
60+
* @return the {@link GetAttr} function.
61+
*/
62+
public final GetAttr getAttr(Identifier path) {
63+
return GetAttr.ofExpressions(toExpression(), path.toString());
64+
}
65+
66+
/**
67+
* Returns an IsSet expression for this instance.
68+
*
69+
* @return the {@link IsSet} function.
70+
*/
71+
public final IsSet isSet() {
72+
return IsSet.ofExpressions(toExpression());
73+
}
74+
75+
/**
76+
* Returns an isValidHostLabel expression of this expression.
77+
*
78+
* @param allowDots whether the UTF-8 {@code .} is considered valid within a host label.
79+
* @return the {@link IsValidHostLabel} function.
80+
*/
81+
public final IsValidHostLabel isValidHostLabel(boolean allowDots) {
82+
return IsValidHostLabel.ofExpressions(toExpression(), allowDots);
83+
}
84+
85+
/**
86+
* Returns a Not expression of this instance.
87+
*
88+
* @return the {@link Not} function.
89+
*/
90+
public final Not not() {
91+
return Not.ofExpressions(toExpression());
92+
}
93+
94+
/**
95+
* Returns a parseUrl expression of this expression.
96+
*
97+
* @return the {@link ParseUrl} function.
98+
*/
99+
public final ParseUrl parseUrl() {
100+
return ParseUrl.ofExpressions(toExpression());
101+
}
102+
103+
/**
104+
* Returns a Substring expression of this expression.
105+
*
106+
* @param startIndex the starting index of the string.
107+
* @param stopIndex the ending index of the string.
108+
* @param reverse whether the indexing is should start from end of the string to start.
109+
* @return the {@link Substring} function.
110+
*/
111+
public final Substring substring(int startIndex, int stopIndex, boolean reverse) {
112+
return Substring.ofExpressions(toExpression(), startIndex, stopIndex, reverse);
113+
}
114+
115+
/**
116+
* Converts this expression to a string template.
117+
* By default, this implementation returns a {@link RuntimeException}.
118+
*
119+
* @return the String template.
120+
*/
121+
public String template() {
122+
throw new RuntimeException(String.format("cannot convert %s to a string template", this));
123+
}
124+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.rulesengine.language.syntax;
7+
8+
import software.amazon.smithy.rulesengine.language.syntax.rule.Condition;
9+
import software.amazon.smithy.utils.SmithyInternalApi;
10+
11+
/**
12+
* Supplies functionality to be coercible into {@link Condition}s for
13+
* use in composing rule-sets in code.
14+
*/
15+
@SmithyInternalApi
16+
public interface ToCondition {
17+
/**
18+
* Convert this into a condition builder for compositional use.
19+
*
20+
* @return the condition builder.
21+
*/
22+
Condition.Builder toConditionBuilder();
23+
24+
/**
25+
* Convert this into a condition.
26+
*
27+
* @return the condition.
28+
*/
29+
default Condition toCondition() {
30+
return toConditionBuilder().build();
31+
}
32+
33+
/**
34+
* Converts this function into a condition which stores the output in the named result.
35+
*
36+
* @param result the name of the result parameter.
37+
* @return the function as a condition.
38+
*/
39+
default Condition toCondition(String result) {
40+
return toConditionBuilder().result(result).build();
41+
}
42+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.rulesengine.language.syntax;
7+
8+
import software.amazon.smithy.rulesengine.language.syntax.expressions.Expression;
9+
import software.amazon.smithy.utils.SmithyInternalApi;
10+
11+
/**
12+
* Supplies functionality to be coercible into {@link Expression}s for
13+
* use in composing rule-sets in code.
14+
*/
15+
@SmithyInternalApi
16+
public interface ToExpression {
17+
/**
18+
* Convert this into an expression.
19+
*
20+
* @return the expression.
21+
*/
22+
Expression toExpression();
23+
}

smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/language/syntax/expressions/Expression.java

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
import software.amazon.smithy.rulesengine.language.evaluation.TypeCheck;
2222
import software.amazon.smithy.rulesengine.language.evaluation.type.Type;
2323
import software.amazon.smithy.rulesengine.language.syntax.Identifier;
24+
import software.amazon.smithy.rulesengine.language.syntax.SyntaxElement;
2425
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.FunctionNode;
2526
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.GetAttr;
26-
import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.Not;
2727
import software.amazon.smithy.rulesengine.language.syntax.expressions.literal.Literal;
28+
import software.amazon.smithy.rulesengine.language.syntax.rule.Condition;
2829
import software.amazon.smithy.utils.SmithyUnstableApi;
2930

3031
/**
@@ -33,7 +34,7 @@
3334
* Expressions are the fundamental building block of the rule language.
3435
*/
3536
@SmithyUnstableApi
36-
public abstract class Expression implements FromSourceLocation, ToNode, TypeCheck {
37+
public abstract class Expression extends SyntaxElement implements FromSourceLocation, ToNode, TypeCheck {
3738
private final SourceLocation sourceLocation;
3839
private Type cachedType;
3940

@@ -65,15 +66,6 @@ public static Literal of(String value) {
6566
return getLiteral(StringNode.from(value));
6667
}
6768

68-
/**
69-
* Negates the current expression.
70-
*
71-
* @return the negated expression.
72-
*/
73-
public Expression not() {
74-
return Not.ofExpressions(this);
75-
}
76-
7769
/**
7870
* Constructs an expression from the provided {@link Node}.
7971
*
@@ -160,14 +152,14 @@ public Type type() {
160152
return cachedType;
161153
}
162154

163-
/**
164-
* Converts this expression to a string template. By default,
165-
* this implementation returns a {@link RuntimeException}.
166-
*
167-
* @return the string template.
168-
*/
169-
public String getTemplate() {
170-
throw new RuntimeException(String.format("Cannot convert `%s` to a string template.", this));
155+
@Override
156+
public Condition.Builder toConditionBuilder() {
157+
return Condition.builder().fn(this);
158+
}
159+
160+
@Override
161+
public Expression toExpression() {
162+
return this;
171163
}
172164

173165
@Override

smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/language/syntax/expressions/Reference.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public Identifier getName() {
4444
}
4545

4646
@Override
47-
public String getTemplate() {
47+
public String template() {
4848
return String.format("{%s}", name);
4949
}
5050

smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/language/syntax/expressions/functions/BooleanEquals.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.List;
1010
import software.amazon.smithy.rulesengine.language.evaluation.type.Type;
1111
import software.amazon.smithy.rulesengine.language.evaluation.value.Value;
12+
import software.amazon.smithy.rulesengine.language.syntax.ToExpression;
1213
import software.amazon.smithy.rulesengine.language.syntax.expressions.Expression;
1314
import software.amazon.smithy.rulesengine.language.syntax.expressions.ExpressionVisitor;
1415
import software.amazon.smithy.utils.SmithyUnstableApi;
@@ -41,10 +42,21 @@ public static Definition getDefinition() {
4142
* @param arg2 the second argument to compare.
4243
* @return The resulting {@link BooleanEquals} function.
4344
*/
44-
public static BooleanEquals ofExpressions(Expression arg1, Expression arg2) {
45+
public static BooleanEquals ofExpressions(ToExpression arg1, ToExpression arg2) {
4546
return DEFINITION.createFunction(FunctionNode.ofExpressions(ID, arg1, arg2));
4647
}
4748

49+
/**
50+
* Creates a {@link BooleanEquals} function from the given expressions.
51+
*
52+
* @param arg1 the first argument to compare.
53+
* @param arg2 the second argument to compare.
54+
* @return The resulting {@link BooleanEquals} function.
55+
*/
56+
public static BooleanEquals ofExpressions(ToExpression arg1, boolean arg2) {
57+
return ofExpressions(arg1, Expression.of(arg2));
58+
}
59+
4860
@Override
4961
public <R> R accept(ExpressionVisitor<R> visitor) {
5062
return visitor.visitBoolEquals(functionNode.getArguments().get(0), functionNode.getArguments().get(1));

0 commit comments

Comments
 (0)