This is an automated email from the ASF dual-hosted git repository.
mdedetrich pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-pekko-connectors.git
The following commit(s) were added to refs/heads/main by this push:
new 5496b3680 Add Scala3 support for Geode
5496b3680 is described below
commit 5496b36809a05d56d114126e2a24538b8b9c2b45
Author: Matthew de Detrich <[email protected]>
AuthorDate: Sat Jun 10 12:33:09 2023 +0200
Add Scala3 support for Geode
Co-Authored-By: Matt Dziuban <[email protected]>
---
build.sbt | 2 +
docs/src/main/paradox/geode.md | 4 +-
.../connectors/geode/impl/pdx/ObjectDecoder.scala | 44 +++++++++++++++
.../connectors/geode/impl/pdx/ObjectEncoder.scala | 40 ++++++++++++++
.../geode/impl/pdx/LabelledGenericGeneric.scala | 64 ++++++++++++++++++++++
.../connectors/geode/impl/pdx/ObjectDecoder.scala | 49 +++++++++++++++++
.../connectors/geode/impl/pdx/ObjectEncoder.scala | 44 +++++++++++++++
.../connectors/geode/impl/pdx/PdxDecoder.scala | 32 +----------
.../connectors/geode/impl/pdx/PdxEncoder.scala | 30 +---------
.../connectors/geode/impl/pdx/PdxWriterMock.scala | 2 +-
project/Dependencies.scala | 12 ++--
11 files changed, 260 insertions(+), 63 deletions(-)
diff --git a/build.sbt b/build.sbt
index 15e782c70..30bfcb0d9 100644
--- a/build.sbt
+++ b/build.sbt
@@ -186,6 +186,8 @@ lazy val geode =
List("-Xlint:-byname-implicit")
case Some((2, n)) if n == 12 =>
List.empty
+ case Some((3, _)) =>
+ List.empty
}
})
diff --git a/docs/src/main/paradox/geode.md b/docs/src/main/paradox/geode.md
index 04d9d3070..522e9dbd7 100644
--- a/docs/src/main/paradox/geode.md
+++ b/docs/src/main/paradox/geode.md
@@ -69,7 +69,9 @@ Java
: @@snip [snip](/geode/src/test/java/docs/javadsl/PersonPdxSerializer.java)
{ #person-pdx-serializer }
-This Apache Pekko Connectors Geode provides a generic solution for Scala users
based on [Shapeless](https://github.com/milessabin/shapeless) which may
generate serializers for case classes at compile time.
+This Apache Pekko Connectors Geode provides a generic solution for Scala users
based on [Shapeless](https://github.com/milessabin/shapeless) for Scala 2 and
+for Scala 3 using the [built-in tuple generic
metaprogramming](https://www.scala-lang.org/2021/02/26/tuples-bring-generic-programming-to-scala-3.html)
+which may generate serializers for case classes at compile time.
Java users need to implement custom serializers manually, or use runtime
reflection as described in @extref[Using Automatic Reflection-Based PDX
Serialization](geode:/developing/data_serialization/auto_serialization.html).
diff --git
a/geode/src/main/scala-2/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectDecoder.scala
b/geode/src/main/scala-2/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectDecoder.scala
new file mode 100644
index 000000000..2fd6852b3
--- /dev/null
+++
b/geode/src/main/scala-2/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectDecoder.scala
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, derived from Akka.
+ */
+
+package org.apache.pekko.stream.connectors.geode.impl.pdx
+
+import org.apache.pekko.annotation.InternalApi
+import shapeless._
+import shapeless.labelled._
+
+import scala.util.{ Failure, Success }
+
+@InternalApi
+private[pekko] trait ObjectDecoder {
+
+ implicit val hnilDecoder: PdxDecoder[HNil] = PdxDecoder.instance((_, _) =>
Success(HNil))
+
+ implicit def hlistDecoder[K <: Symbol, H, T <: HList](
+ implicit witness: Witness.Aux[K],
+ hDecoder: Lazy[PdxDecoder[H]],
+ tDecoder: Lazy[PdxDecoder[T]]): PdxDecoder[FieldType[K, H] :: T] =
PdxDecoder.instance {
+ case (reader, fieldName) => {
+ val headField = hDecoder.value.decode(reader, witness.value)
+ val tailFields = tDecoder.value.decode(reader, fieldName)
+ (headField, tailFields) match {
+ case (Success(h), Success(t)) => Success(field[K](h) :: t)
+ case _ => Failure(null)
+ }
+ }
+ case e => Failure(null)
+ }
+
+ implicit def objectDecoder[A, Repr <: HList](
+ implicit gen: LabelledGeneric.Aux[A, Repr],
+ hlistDecoder: PdxDecoder[Repr]): PdxDecoder[A] = PdxDecoder.instance {
(reader, fieldName) =>
+ hlistDecoder.decode(reader, fieldName).map(gen.from)
+ }
+
+}
diff --git
a/geode/src/main/scala-2/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectEncoder.scala
b/geode/src/main/scala-2/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectEncoder.scala
new file mode 100644
index 000000000..794e6c50f
--- /dev/null
+++
b/geode/src/main/scala-2/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectEncoder.scala
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, derived from Akka.
+ */
+
+package org.apache.pekko.stream.connectors.geode.impl.pdx
+
+import org.apache.pekko.annotation.InternalApi
+import shapeless._
+import shapeless.labelled._
+import shapeless.ops.hlist.IsHCons
+
+@InternalApi
+private[pekko] trait ObjectEncoder {
+
+ implicit val hnilEncoder: PdxEncoder[HNil] =
+ PdxEncoder.instance[HNil] { case _ => true }
+ implicit def hlistEncoder[K <: Symbol, H, T <: shapeless.HList](
+ implicit witness: Witness.Aux[K],
+ isHCons: IsHCons.Aux[H :: T, H, T],
+ hEncoder: Lazy[PdxEncoder[H]],
+ tEncoder: Lazy[PdxEncoder[T]]): PdxEncoder[FieldType[K, H] :: T] =
+ PdxEncoder.instance[FieldType[K, H] :: T] {
+ case (writer, o, fieldName) =>
+ hEncoder.value.encode(writer, isHCons.head(o), witness.value)
+ tEncoder.value.encode(writer, isHCons.tail(o), fieldName)
+ }
+
+ implicit def objectEncoder[A, Repr <: HList](
+ implicit gen: LabelledGeneric.Aux[A, Repr],
+ hlistEncoder: Lazy[PdxEncoder[Repr]]): PdxEncoder[A] =
PdxEncoder.instance {
+ case (writer, o, fieldName) =>
+ hlistEncoder.value.encode(writer, gen.to(o), fieldName)
+ }
+
+}
diff --git
a/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/LabelledGenericGeneric.scala
b/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/LabelledGenericGeneric.scala
new file mode 100644
index 000000000..835aac0e1
--- /dev/null
+++
b/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/LabelledGenericGeneric.scala
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.pekko.stream.connectors.geode.impl.pdx
+
+import org.apache.pekko.annotation.InternalApi
+
+import scala.deriving.Mirror
+
+@InternalApi
+private[pekko] type FieldType[K, +V] = V with KeyTag[K, V]
+
+@InternalApi
+private[pekko] object FieldType {
+ def label[K]: [V] => V => FieldType[K, V] = [V] => (v: V) =>
v.asInstanceOf[FieldType[K, V]]
+}
+
+@InternalApi
+private[pekko] type KeyTag[K, +V]
+
+@InternalApi
+private[pekko] type ZipWith[T1 <: Tuple, T2 <: Tuple, F[_, _]] <: Tuple = (T1,
T2) match {
+ case (h1 *: t1, h2 *: t2) => F[h1, h2] *: ZipWith[t1, t2, F]
+ case (EmptyTuple, ?) => EmptyTuple
+ case (?, EmptyTuple) => EmptyTuple
+ case _ => Tuple
+}
+
+@InternalApi
+private[pekko] trait LabelledGeneric[A] {
+ type Repr
+ def from(r: Repr): A
+ def to(a: A): Repr
+}
+
+@InternalApi
+private[pekko] object LabelledGeneric {
+ type Aux[A, R] = LabelledGeneric[A] { type Repr = R }
+
+ inline def apply[A](using l: LabelledGeneric[A]): LabelledGeneric.Aux[A,
l.Repr] = l
+
+ inline given productInst[A <: Product](
+ using m: Mirror.ProductOf[A])
+ : LabelledGeneric.Aux[A, ZipWith[m.MirroredElemLabels,
m.MirroredElemTypes, FieldType]] =
+ new LabelledGeneric[A] {
+ type Repr = Tuple & ZipWith[m.MirroredElemLabels, m.MirroredElemTypes,
FieldType]
+ def from(r: Repr): A = m.fromTuple(r.asInstanceOf[m.MirroredElemTypes])
+ def to(a: A): Repr = Tuple.fromProductTyped(a).asInstanceOf[Repr]
+ }
+}
diff --git
a/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectDecoder.scala
b/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectDecoder.scala
new file mode 100644
index 000000000..a586e7ed6
--- /dev/null
+++
b/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectDecoder.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.pekko.stream.connectors.geode.impl.pdx
+
+import org.apache.pekko.annotation.InternalApi
+
+import scala.util.{ Failure, Success }
+
+@InternalApi
+private[pekko] trait ObjectDecoder {
+
+ given emptyTupleDecoder: PdxDecoder[EmptyTuple] = PdxDecoder.instance((_, _)
=> Success(EmptyTuple))
+
+ given tupleDecoder[K <: String, H, T <: Tuple](
+ using m: ValueOf[K],
+ hDecoder: PdxDecoder[H],
+ tDecoder: PdxDecoder[T]): PdxDecoder[FieldType[K, H] *: T] =
PdxDecoder.instance {
+ case (reader, fieldName) => {
+ val headField = hDecoder.decode(reader, Symbol(m.value))
+ val tailFields = tDecoder.decode(reader, fieldName)
+ (headField, tailFields) match {
+ case (Success(h), Success(t)) => Success(FieldType.label[K](h) *: t)
+ case _ => Failure(null)
+ }
+ }
+ case e => Failure(null)
+ }
+
+ given objectDecoder[A, Repr <: Tuple](
+ using gen: LabelledGeneric.Aux[A, Repr],
+ tupleDecoder: PdxDecoder[Repr]): PdxDecoder[A] = PdxDecoder.instance {
(reader, fieldName) =>
+ tupleDecoder.decode(reader, fieldName).map(gen.from)
+ }
+}
diff --git
a/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectEncoder.scala
b/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectEncoder.scala
new file mode 100644
index 000000000..fea86fb49
--- /dev/null
+++
b/geode/src/main/scala-3/org/apache/pekko/stream/connectors/geode/impl/pdx/ObjectEncoder.scala
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.pekko.stream.connectors.geode.impl.pdx
+
+import org.apache.pekko.annotation.InternalApi
+
+@InternalApi
+private[pekko] trait ObjectEncoder {
+
+ given emptyTupleEncoder: PdxEncoder[EmptyTuple] =
+ PdxEncoder.instance[EmptyTuple] { case _ => true }
+
+ given tupleEncoder[K <: String, H, T <: Tuple](using
+ m: ValueOf[K],
+ hEncoder: PdxEncoder[H],
+ tEncoder: PdxEncoder[T]): PdxEncoder[FieldType[K, H] *: T] =
+ PdxEncoder.instance[FieldType[K, H] *: T] {
+ case (writer, o, fieldName) =>
+ hEncoder.encode(writer, o.head, Symbol(m.value))
+ tEncoder.encode(writer, o.tail, fieldName)
+ }
+
+ given objectEncoder[A, Repr <: Tuple](
+ using gen: LabelledGeneric.Aux[A, Repr],
+ tupleEncoder: PdxEncoder[Repr]): PdxEncoder[A] = PdxEncoder.instance {
+ case (writer, o, fieldName) =>
+ tupleEncoder.encode(writer, gen.to(o), fieldName)
+ }
+}
diff --git
a/geode/src/main/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxDecoder.scala
b/geode/src/main/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxDecoder.scala
index 40fea422b..f3fb18d21 100644
---
a/geode/src/main/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxDecoder.scala
+++
b/geode/src/main/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxDecoder.scala
@@ -18,7 +18,7 @@ import java.util.{ Date, UUID }
import org.apache.pekko.annotation.InternalApi
import org.apache.geode.pdx.PdxReader
-import scala.util.{ Failure, Success, Try }
+import scala.util.{ Success, Try }
@InternalApi
trait PdxDecoder[A] {
@@ -27,18 +27,13 @@ trait PdxDecoder[A] {
}
-object PdxDecoder {
+object PdxDecoder extends ObjectDecoder {
- import shapeless._
- import shapeless.labelled._
-
- private def instance[A](f: (PdxReader, Symbol) => Try[A]): PdxDecoder[A] =
+ private[pekko] def instance[A](f: (PdxReader, Symbol) => Try[A]):
PdxDecoder[A] =
new PdxDecoder[A] {
def decode(reader: PdxReader, fieldName: Symbol) = f(reader, fieldName)
}
- implicit val hnilDecoder: PdxDecoder[HNil] = instance((_, _) =>
Success(HNil))
-
implicit val booleanDecoder: PdxDecoder[Boolean] = instance {
case (reader, fieldName) =>
Success(reader.readBoolean(fieldName.name))
@@ -149,27 +144,6 @@ object PdxDecoder {
Try(reader.readObjectArray(fieldName.name).toSet.asInstanceOf[Set[T]])
}
- implicit def hlistDecoder[K <: Symbol, H, T <: HList](
- implicit witness: Witness.Aux[K],
- hDecoder: Lazy[PdxDecoder[H]],
- tDecoder: Lazy[PdxDecoder[T]]): PdxDecoder[FieldType[K, H] :: T] =
instance {
- case (reader, fieldName) => {
- val headField = hDecoder.value.decode(reader, witness.value)
- val tailFields = tDecoder.value.decode(reader, fieldName)
- (headField, tailFields) match {
- case (Success(h), Success(t)) => Success(field[K](h) :: t)
- case _ => Failure(null)
- }
- }
- case e => Failure(null)
- }
-
- implicit def objectDecoder[A, Repr <: HList](
- implicit gen: LabelledGeneric.Aux[A, Repr],
- hlistDecoder: PdxDecoder[Repr]): PdxDecoder[A] = instance { (reader,
fieldName) =>
- hlistDecoder.decode(reader, fieldName).map(gen.from)
- }
-
def apply[A](implicit ev: PdxDecoder[A]): PdxDecoder[A] = ev
}
diff --git
a/geode/src/main/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxEncoder.scala
b/geode/src/main/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxEncoder.scala
index e9e853505..61758c503 100644
---
a/geode/src/main/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxEncoder.scala
+++
b/geode/src/main/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxEncoder.scala
@@ -17,45 +17,19 @@ import java.util.{ Date, UUID }
import org.apache.pekko.annotation.InternalApi
import org.apache.geode.pdx.PdxWriter
-import shapeless.ops.hlist.IsHCons
@InternalApi
trait PdxEncoder[A] {
def encode(writer: PdxWriter, a: A, fieldName: Symbol = null): Boolean
}
-object PdxEncoder {
+object PdxEncoder extends ObjectEncoder {
- import shapeless._
- import shapeless.labelled._
-
- private def instance[A](f: (PdxWriter, A, Symbol) => Boolean) =
+ private[pekko] def instance[A](f: (PdxWriter, A, Symbol) => Boolean) =
new PdxEncoder[A] {
def encode(writer: PdxWriter, a: A, fieldName: Symbol = null): Boolean =
f(writer, a, fieldName)
}
- implicit val hnilEncoder: PdxEncoder[HNil] =
- instance[HNil] { case _ => true }
-
- implicit def hlistEncoder[K <: Symbol, H, T <: shapeless.HList](
- implicit witness: Witness.Aux[K],
- isHCons: IsHCons.Aux[H :: T, H, T],
- hEncoder: Lazy[PdxEncoder[H]],
- tEncoder: Lazy[PdxEncoder[T]]): PdxEncoder[FieldType[K, H] :: T] =
- instance[FieldType[K, H] :: T] {
- case (writer, o, fieldName) =>
- hEncoder.value.encode(writer, isHCons.head(o), witness.value)
- tEncoder.value.encode(writer, isHCons.tail(o), fieldName)
-
- }
-
- implicit def objectEncoder[A, Repr <: HList](
- implicit gen: LabelledGeneric.Aux[A, Repr],
- hlistEncoder: Lazy[PdxEncoder[Repr]]): PdxEncoder[A] = instance {
- case (writer, o, fieldName) =>
- hlistEncoder.value.encode(writer, gen.to(o), fieldName)
- }
-
def apply[A](implicit enc: PdxEncoder[A]): PdxEncoder[A] = enc
implicit def booleanEncoder: PdxEncoder[Boolean] = instance {
diff --git
a/geode/src/test/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxWriterMock.scala
b/geode/src/test/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxWriterMock.scala
index 2152ecd89..cdd377ff4 100644
---
a/geode/src/test/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxWriterMock.scala
+++
b/geode/src/test/scala/org/apache/pekko/stream/connectors/geode/impl/pdx/PdxWriterMock.scala
@@ -88,5 +88,5 @@ object PdxMocks {
override def writeByte(fieldName: String, value: Byte) = { println(s"Write
$value"); this }
}
- implicit val writerMock = new WriterMock()
+ implicit val writerMock: WriterMock = new WriterMock()
}
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 9ec4c475f..d3183886c 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -8,6 +8,7 @@
*/
import sbt._
+import Common.isScala3
import Keys._
object Dependencies {
@@ -178,13 +179,16 @@ object Dependencies {
val GeodeVersionForDocs = "115"
val Geode = Seq(
- crossScalaVersions -= Scala3,
- libraryDependencies ++=
+ libraryDependencies ++= {
Seq("geode-core", "geode-cq")
.map("org.apache.geode" % _ % GeodeVersion) ++
Seq(
- "com.chuusai" %% "shapeless" % "2.3.3",
- "org.apache.logging.log4j" % "log4j-to-slf4j" % "2.17.1" % Test) ++
JacksonDatabindDependencies)
+ "org.apache.logging.log4j" % "log4j-to-slf4j" % "2.17.1" % Test) ++
JacksonDatabindDependencies ++
+ (if (isScala3.value)
+ Seq.empty // Equivalent and relevant shapeless functionality has been
mainlined into Scala 3 language/stdlib
+ else Seq(
+ "com.chuusai" %% "shapeless" % "2.3.10"))
+ })
val GoogleCommon = Seq(
libraryDependencies ++= Seq(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]