diff --git a/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java b/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java index db3a6286..58780e44 100644 --- a/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java +++ b/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java @@ -36,6 +36,7 @@ public final class CelMutableExpr { private CelNotSet notSet; private CelConstant constant; private CelMutableIdent ident; + private CelMutableSelect select; private int hash = 0; public long id() { @@ -65,6 +66,11 @@ public CelMutableIdent ident() { return ident; } + public CelMutableSelect select() { + checkExprKind(Kind.SELECT); + return select; + } + public void setConstant(CelConstant constant) { this.exprKind = ExprKind.Kind.CONSTANT; this.constant = checkNotNull(constant); @@ -75,6 +81,11 @@ public void setIdent(CelMutableIdent ident) { this.ident = checkNotNull(ident); } + public void setSelect(CelMutableSelect select) { + this.exprKind = ExprKind.Kind.SELECT; + this.select = checkNotNull(select); + } + /** A mutable identifier expression. */ public static final class CelMutableIdent { private String name = ""; @@ -114,6 +125,77 @@ private CelMutableIdent(String name) { } } + /** A mutable field selection expression. e.g. `request.auth`. */ + public static final class CelMutableSelect { + private CelMutableExpr operand; + private String field = ""; + private boolean testOnly; + + public CelMutableExpr operand() { + return operand; + } + + public void setOperand(CelMutableExpr operand) { + this.operand = checkNotNull(operand); + } + + public String field() { + return field; + } + + public void setField(String field) { + this.field = checkNotNull(field); + } + + public boolean testOnly() { + return testOnly; + } + + public void setTestOnly(boolean testOnly) { + this.testOnly = testOnly; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof CelMutableSelect) { + CelMutableSelect that = (CelMutableSelect) obj; + return this.operand.equals(that.operand()) + && this.field.equals(that.field()) + && this.testOnly == that.testOnly(); + } + return false; + } + + @Override + public int hashCode() { + int h = 1; + h *= 1000003; + h ^= operand.hashCode(); + h *= 1000003; + h ^= field.hashCode(); + h *= 1000003; + h ^= testOnly ? 1231 : 1237; + return h; + } + + public static CelMutableSelect create(CelMutableExpr operand, String field) { + return new CelMutableSelect(operand, field, false); + } + + public static CelMutableSelect create(CelMutableExpr operand, String field, boolean testOnly) { + return new CelMutableSelect(operand, field, testOnly); + } + + private CelMutableSelect(CelMutableExpr operand, String field, boolean testOnly) { + this.operand = checkNotNull(operand); + this.field = checkNotNull(field); + this.testOnly = testOnly; + } + } + public static CelMutableExpr ofNotSet() { return ofNotSet(0L); } @@ -138,6 +220,14 @@ public static CelMutableExpr ofIdent(long id, String name) { return new CelMutableExpr(id, CelMutableIdent.create(name)); } + public static CelMutableExpr ofSelect(CelMutableSelect mutableSelect) { + return ofSelect(0, mutableSelect); + } + + public static CelMutableExpr ofSelect(long id, CelMutableSelect mutableSelect) { + return new CelMutableExpr(id, mutableSelect); + } + private CelMutableExpr(long id, CelConstant mutableConstant) { this.id = id; setConstant(mutableConstant); @@ -148,6 +238,11 @@ private CelMutableExpr(long id, CelMutableIdent mutableIdent) { setIdent(mutableIdent); } + private CelMutableExpr(long id, CelMutableSelect mutableSelect) { + this.id = id; + setSelect(mutableSelect); + } + private CelMutableExpr(long id) { this(); this.id = id; @@ -167,6 +262,7 @@ private Object exprValue() { case IDENT: return ident(); case SELECT: + return select(); case CALL: case CREATE_LIST: case CREATE_STRUCT: diff --git a/common/src/test/java/dev/cel/common/ast/CelMutableExprTest.java b/common/src/test/java/dev/cel/common/ast/CelMutableExprTest.java index 3d44ac85..73d595c6 100644 --- a/common/src/test/java/dev/cel/common/ast/CelMutableExprTest.java +++ b/common/src/test/java/dev/cel/common/ast/CelMutableExprTest.java @@ -22,6 +22,7 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector; import dev.cel.common.ast.CelExpr.ExprKind.Kind; import dev.cel.common.ast.CelMutableExpr.CelMutableIdent; +import dev.cel.common.ast.CelMutableExpr.CelMutableSelect; import org.junit.Test; import org.junit.runner.RunWith; @@ -85,6 +86,44 @@ public void mutableIdent_setName() { assertThat(ident.name()).isEqualTo("y"); } + @Test + public void ofSelect() { + CelMutableExpr mutableExpr = + CelMutableExpr.ofSelect(CelMutableSelect.create(CelMutableExpr.ofIdent("x"), "field")); + + assertThat(mutableExpr.id()).isEqualTo(0L); + assertThat(mutableExpr.select().testOnly()).isFalse(); + assertThat(mutableExpr.select().field()).isEqualTo("field"); + assertThat(mutableExpr.select().operand()).isEqualTo(CelMutableExpr.ofIdent("x")); + } + + @Test + public void ofSelect_withId() { + CelMutableExpr mutableExpr = + CelMutableExpr.ofSelect( + 1L, + CelMutableSelect.create(CelMutableExpr.ofIdent("x"), "field", /* testOnly= */ true)); + + assertThat(mutableExpr.id()).isEqualTo(1L); + assertThat(mutableExpr.select().testOnly()).isTrue(); + assertThat(mutableExpr.select().field()).isEqualTo("field"); + assertThat(mutableExpr.select().operand()).isEqualTo(CelMutableExpr.ofIdent("x")); + } + + @Test + public void mutableSelect_setters() { + CelMutableSelect select = + CelMutableSelect.create(CelMutableExpr.ofIdent("x"), "field", /* testOnly= */ true); + + select.setOperand(CelMutableExpr.ofConstant(CelConstant.ofValue(1L))); + select.setField("field2"); + select.setTestOnly(false); + + assertThat(select.operand()).isEqualTo(CelMutableExpr.ofConstant(CelConstant.ofValue(1L))); + assertThat(select.field()).isEqualTo("field2"); + assertThat(select.testOnly()).isFalse(); + } + @Test public void setId_success() { CelMutableExpr mutableExpr = CelMutableExpr.ofConstant(CelConstant.ofValue(5L)); @@ -105,6 +144,13 @@ public void equalityTest() { CelMutableExpr.ofConstant(5L, CelConstant.ofValue("hello"))) .addEqualityGroup(CelMutableExpr.ofIdent("x")) .addEqualityGroup(CelMutableExpr.ofIdent(2L, "y"), CelMutableExpr.ofIdent(2L, "y")) + .addEqualityGroup( + CelMutableExpr.ofSelect(CelMutableSelect.create(CelMutableExpr.ofIdent("y"), "field"))) + .addEqualityGroup( + CelMutableExpr.ofSelect( + 4L, CelMutableSelect.create(CelMutableExpr.ofIdent("x"), "test")), + CelMutableExpr.ofSelect( + 4L, CelMutableSelect.create(CelMutableExpr.ofIdent("x"), "test"))) .testEquals(); } @@ -113,6 +159,7 @@ private enum MutableExprKindTestCase { NOT_SET(CelMutableExpr.ofNotSet(1L)), CONSTANT(CelMutableExpr.ofConstant(CelConstant.ofValue(2L))), IDENT(CelMutableExpr.ofIdent("test")), + SELECT(CelMutableExpr.ofSelect(CelMutableSelect.create(CelMutableExpr.ofNotSet(), "field"))), ; private final CelMutableExpr mutableExpr; @@ -134,6 +181,9 @@ public void getExprValue_invalidKind_throws(@TestParameter MutableExprKindTestCa if (!testCaseKind.equals(Kind.IDENT)) { assertThrows(IllegalArgumentException.class, testCase.mutableExpr::ident); } + if (!testCaseKind.equals(Kind.SELECT)) { + assertThrows(IllegalArgumentException.class, testCase.mutableExpr::select); + } } @SuppressWarnings("Immutable") // Mutable by design @@ -141,6 +191,11 @@ private enum HashCodeTestCase { NOT_SET(CelMutableExpr.ofNotSet(1L), -722379961), CONSTANT(CelMutableExpr.ofConstant(2L, CelConstant.ofValue("test")), -724279919), IDENT(CelMutableExpr.ofIdent("x"), -721379855), + SELECT( + CelMutableExpr.ofSelect( + 4L, + CelMutableSelect.create(CelMutableExpr.ofIdent("y"), "field", /* testOnly= */ true)), + 1458249843), ; private final CelMutableExpr mutableExpr;