diff --git a/common/src/main/java/dev/cel/common/values/BUILD.bazel b/common/src/main/java/dev/cel/common/values/BUILD.bazel index 8dbdbe95..38160882 100644 --- a/common/src/main/java/dev/cel/common/values/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/values/BUILD.bazel @@ -7,6 +7,45 @@ package( ], ) +# keep sorted +CEL_VALUES_SOURCES = [ + "BoolValue.java", + "BytesValue.java", + "DoubleValue.java", + "DurationValue.java", + "EnumValue.java", + "IntValue.java", + "NullValue.java", + "StringValue.java", + "TimestampValue.java", + "UintValue.java", +] + +java_library( + name = "cel_value", + srcs = ["CelValue.java"], + tags = [ + ], + deps = [ + "//common/annotations", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + +java_library( + name = "values", + srcs = CEL_VALUES_SOURCES, + tags = [ + ], + deps = [ + ":cel_byte_string", + ":cel_value", + "//:auto_value", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven//:com_google_guava_guava", + ], +) + java_library( name = "cel_byte_string", srcs = ["CelByteString.java"], diff --git a/common/src/main/java/dev/cel/common/values/BoolValue.java b/common/src/main/java/dev/cel/common/values/BoolValue.java new file mode 100644 index 00000000..0fe868cc --- /dev/null +++ b/common/src/main/java/dev/cel/common/values/BoolValue.java @@ -0,0 +1,36 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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 dev.cel.common.values; + +import com.google.auto.value.AutoValue; +import com.google.errorprone.annotations.Immutable; + +/** BoolValue is a simple CelValue wrapper around Java booleans. */ +@AutoValue +@Immutable +public abstract class BoolValue extends CelValue { + + @Override + public abstract Boolean value(); + + @Override + public boolean isZeroValue() { + return !value(); + } + + public static BoolValue create(Boolean value) { + return new AutoValue_BoolValue(value); + } +} diff --git a/common/src/main/java/dev/cel/common/values/BytesValue.java b/common/src/main/java/dev/cel/common/values/BytesValue.java new file mode 100644 index 00000000..dad78736 --- /dev/null +++ b/common/src/main/java/dev/cel/common/values/BytesValue.java @@ -0,0 +1,36 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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 dev.cel.common.values; + +import com.google.auto.value.AutoValue; +import com.google.errorprone.annotations.Immutable; + +/** BytesValue is a simple CelValue wrapper around CelByteString (immutable byte string). */ +@AutoValue +@Immutable +public abstract class BytesValue extends CelValue { + + @Override + public abstract CelByteString value(); + + @Override + public boolean isZeroValue() { + return value().isEmpty(); + } + + public static BytesValue create(CelByteString value) { + return new AutoValue_BytesValue(value); + } +} diff --git a/common/src/main/java/dev/cel/common/values/CelValue.java b/common/src/main/java/dev/cel/common/values/CelValue.java new file mode 100644 index 00000000..582338d9 --- /dev/null +++ b/common/src/main/java/dev/cel/common/values/CelValue.java @@ -0,0 +1,42 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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 dev.cel.common.values; + +import com.google.errorprone.annotations.Immutable; +import dev.cel.common.annotations.Internal; + +/** + * A representation of a CEL value for the runtime. Clients should never directly extend from + * CelValue. Clients may extend some subclasses of CelValue, such as StructValue if it has been + * explicitly designed and documented for extension. + */ +@Immutable +@Internal +public abstract class CelValue { + + /** + * The underlying value. This is typically the Java native value or a derived instance of CelValue + * (ex: an element in lists or key/value pair in maps). + */ + public abstract Object value(); + + /** Returns true if the {@link #value()} is a zero value for its type. */ + public abstract boolean isZeroValue(); + + // TOOD(b/309695452): Add CelEquals method + // TODO: Add a getter for CelType + + public CelValue() {} +} diff --git a/common/src/main/java/dev/cel/common/values/DoubleValue.java b/common/src/main/java/dev/cel/common/values/DoubleValue.java new file mode 100644 index 00000000..952d27c3 --- /dev/null +++ b/common/src/main/java/dev/cel/common/values/DoubleValue.java @@ -0,0 +1,67 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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 dev.cel.common.values; + +import com.google.errorprone.annotations.Immutable; + +/** DoubleValue is a simple CelValue wrapper around Java doubles. */ +@Immutable +public final class DoubleValue extends CelValue { + private final double value; + + @Override + public Double value() { + return value; + } + + public double doubleValue() { + return value; + } + + @Override + public boolean isZeroValue() { + return value() == 0; + } + + public static DoubleValue create(double value) { + return new DoubleValue(value); + } + + @Override + public int hashCode() { + int h = 1; + h *= 1000003; + h ^= Double.hashCode(value); + return h; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof DoubleValue)) { + return false; + } + + return Double.doubleToLongBits(((DoubleValue) o).doubleValue()) + == Double.doubleToLongBits(this.doubleValue()); + } + + private DoubleValue(double value) { + this.value = value; + } +} diff --git a/common/src/main/java/dev/cel/common/values/DurationValue.java b/common/src/main/java/dev/cel/common/values/DurationValue.java new file mode 100644 index 00000000..f706d30a --- /dev/null +++ b/common/src/main/java/dev/cel/common/values/DurationValue.java @@ -0,0 +1,37 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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 dev.cel.common.values; + +import com.google.auto.value.AutoValue; +import com.google.errorprone.annotations.Immutable; +import java.time.Duration; + +/** DurationValue is a simple CelValue wrapper around {@link java.time.Duration} */ +@AutoValue +@Immutable +public abstract class DurationValue extends CelValue { + + @Override + public abstract Duration value(); + + @Override + public boolean isZeroValue() { + return value().isZero(); + } + + public static DurationValue create(Duration value) { + return new AutoValue_DurationValue(value); + } +} diff --git a/common/src/main/java/dev/cel/common/values/EnumValue.java b/common/src/main/java/dev/cel/common/values/EnumValue.java new file mode 100644 index 00000000..9aeaacd7 --- /dev/null +++ b/common/src/main/java/dev/cel/common/values/EnumValue.java @@ -0,0 +1,41 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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 dev.cel.common.values; + +import com.google.auto.value.AutoValue; +import com.google.errorprone.annotations.Immutable; + +/** + * EnumValue is a simple CelValue wrapper around Java enums. + * + *
Note: CEL-Java currently does not support strongly typed enum. This value class will not be
+ * used until the said support is added in.
+ */
+@AutoValue
+@Immutable(containerOf = "E")
+public abstract class EnumValue TODO: Look into potentially accepting a primitive `long` to avoid boxing/unboxing
+ * when the interpreter is augmented to work directly on CelValue.
+ */
+@AutoValue
+@Immutable
+public abstract class UintValue extends CelValue {
+
+ @Override
+ public abstract UnsignedLong value();
+
+ @Override
+ public boolean isZeroValue() {
+ return UnsignedLong.ZERO.equals(value());
+ }
+
+ public static UintValue create(UnsignedLong value) {
+ return new AutoValue_UintValue(value);
+ }
+}
diff --git a/common/src/test/java/dev/cel/common/values/BUILD.bazel b/common/src/test/java/dev/cel/common/values/BUILD.bazel
index e60b124c..844004ac 100644
--- a/common/src/test/java/dev/cel/common/values/BUILD.bazel
+++ b/common/src/test/java/dev/cel/common/values/BUILD.bazel
@@ -8,7 +8,9 @@ java_library(
srcs = glob(["*.java"]),
deps = [
"//:java_truth",
+ "//common/values",
"//common/values:cel_byte_string",
+ "@maven//:com_google_guava_guava",
"@maven//:com_google_guava_guava_testlib",
"@maven//:junit_junit",
],
diff --git a/common/src/test/java/dev/cel/common/values/BoolValueTest.java b/common/src/test/java/dev/cel/common/values/BoolValueTest.java
new file mode 100644
index 00000000..ada0e442
--- /dev/null
+++ b/common/src/test/java/dev/cel/common/values/BoolValueTest.java
@@ -0,0 +1,47 @@
+// Copyright 2023 Google LLC
+//
+// 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
+//
+// https://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 dev.cel.common.values;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BoolValueTest {
+
+ @Test
+ public void falseBool() {
+ BoolValue boolValue = BoolValue.create(false);
+
+ assertThat(boolValue.value()).isFalse();
+ assertThat(boolValue.isZeroValue()).isTrue();
+ }
+
+ @Test
+ public void trueBool() {
+ BoolValue boolValue = BoolValue.create(true);
+
+ assertThat(boolValue.value()).isTrue();
+ assertThat(boolValue.isZeroValue()).isFalse();
+ }
+
+ @Test
+ public void create_nullValue_throws() {
+ assertThrows(NullPointerException.class, () -> BoolValue.create(null));
+ }
+}
diff --git a/common/src/test/java/dev/cel/common/values/BytesValueTest.java b/common/src/test/java/dev/cel/common/values/BytesValueTest.java
new file mode 100644
index 00000000..019dfc8e
--- /dev/null
+++ b/common/src/test/java/dev/cel/common/values/BytesValueTest.java
@@ -0,0 +1,47 @@
+// Copyright 2023 Google LLC
+//
+// 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
+//
+// https://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 dev.cel.common.values;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BytesValueTest {
+
+ @Test
+ public void emptyBytes() {
+ BytesValue bytesValue = BytesValue.create(CelByteString.EMPTY);
+
+ assertThat(bytesValue.value()).isEqualTo(CelByteString.of(new byte[0]));
+ assertThat(bytesValue.isZeroValue()).isTrue();
+ }
+
+ @Test
+ public void constructBytes() {
+ BytesValue bytesValue = BytesValue.create(CelByteString.of(new byte[] {0x1, 0x5, 0xc}));
+
+ assertThat(bytesValue.value()).isEqualTo(CelByteString.of(new byte[] {0x1, 0x5, 0xc}));
+ assertThat(bytesValue.isZeroValue()).isFalse();
+ }
+
+ @Test
+ public void create_nullValue_throws() {
+ assertThrows(NullPointerException.class, () -> BytesValue.create(null));
+ }
+}
diff --git a/common/src/test/java/dev/cel/common/values/DoubleValueTest.java b/common/src/test/java/dev/cel/common/values/DoubleValueTest.java
new file mode 100644
index 00000000..9d0f3263
--- /dev/null
+++ b/common/src/test/java/dev/cel/common/values/DoubleValueTest.java
@@ -0,0 +1,76 @@
+// Copyright 2023 Google LLC
+//
+// 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
+//
+// https://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 dev.cel.common.values;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.testing.ClassSanityTester;
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DoubleValueTest {
+
+ @Test
+ public void emptyDouble() {
+ DoubleValue doubleValue = DoubleValue.create(0.0d);
+
+ assertThat(doubleValue.value()).isEqualTo(0.0d);
+ assertThat(doubleValue.isZeroValue()).isTrue();
+ }
+
+ @Test
+ public void constructDouble() {
+ DoubleValue doubleValue = DoubleValue.create(5.0d);
+
+ assertThat(doubleValue.value()).isEqualTo(5.0d);
+ assertThat(doubleValue.isZeroValue()).isFalse();
+ }
+
+ @Test
+ public void equalityTest() {
+ new EqualsTester()
+ .addEqualityGroup(DoubleValue.create(10.5))
+ .addEqualityGroup(DoubleValue.create(0.0d), DoubleValue.create(0))
+ .addEqualityGroup(DoubleValue.create(15.3), DoubleValue.create(15.3))
+ .addEqualityGroup(
+ DoubleValue.create(Double.MAX_VALUE), DoubleValue.create(Double.MAX_VALUE))
+ .addEqualityGroup(
+ DoubleValue.create(Double.MIN_VALUE), DoubleValue.create(Double.MIN_VALUE))
+ .testEquals();
+ }
+
+ @Test
+ public void sanityTest() throws Exception {
+ new ClassSanityTester()
+ .setDefault(DoubleValue.class, DoubleValue.create(100.94d))
+ .setDistinctValues(DoubleValue.class, DoubleValue.create(0.0d), DoubleValue.create(100.0d))
+ .forAllPublicStaticMethods(DoubleValue.class)
+ .thatReturn(DoubleValue.class)
+ .testEquals()
+ .testNulls();
+ }
+
+ @Test
+ public void hashCode_smokeTest() {
+ assertThat(DoubleValue.create(0).hashCode()).isEqualTo(1000003);
+ assertThat(DoubleValue.create(0.0d).hashCode()).isEqualTo(1000003);
+ assertThat(DoubleValue.create(100.5d).hashCode()).isEqualTo(1079403075);
+ assertThat(DoubleValue.create(Double.MAX_VALUE).hashCode()).isEqualTo(-2145435069);
+ assertThat(DoubleValue.create(Double.MIN_VALUE).hashCode()).isEqualTo(1000002);
+ }
+}
diff --git a/common/src/test/java/dev/cel/common/values/DurationValueTest.java b/common/src/test/java/dev/cel/common/values/DurationValueTest.java
new file mode 100644
index 00000000..8ad721cb
--- /dev/null
+++ b/common/src/test/java/dev/cel/common/values/DurationValueTest.java
@@ -0,0 +1,48 @@
+// Copyright 2023 Google LLC
+//
+// 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
+//
+// https://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 dev.cel.common.values;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import java.time.Duration;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DurationValueTest {
+
+ @Test
+ public void emptyDuration() {
+ DurationValue durationValue = DurationValue.create(Duration.ZERO);
+
+ assertThat(durationValue.value()).isEqualTo(Duration.ofSeconds(0));
+ assertThat(durationValue.isZeroValue()).isTrue();
+ }
+
+ @Test
+ public void constructDuration() {
+ DurationValue durationValue = DurationValue.create(Duration.ofSeconds(10000));
+
+ assertThat(durationValue.value()).isEqualTo(Duration.ofSeconds(10000));
+ assertThat(durationValue.isZeroValue()).isFalse();
+ }
+
+ @Test
+ public void create_nullValue_throws() {
+ assertThrows(NullPointerException.class, () -> DurationValue.create(null));
+ }
+}
diff --git a/common/src/test/java/dev/cel/common/values/EnumValueTest.java b/common/src/test/java/dev/cel/common/values/EnumValueTest.java
new file mode 100644
index 00000000..d10c6cf3
--- /dev/null
+++ b/common/src/test/java/dev/cel/common/values/EnumValueTest.java
@@ -0,0 +1,45 @@
+// Copyright 2023 Google LLC
+//
+// 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
+//
+// https://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 dev.cel.common.values;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EnumValueTest {
+
+ private enum TestKind {
+ ONE,
+ TWO
+ }
+
+ @Test
+ public void enumValue_construct() {
+ EnumValue