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]

Reply via email to