Skip to content

Commit 4127bc8

Browse files
committed
add-scala3-enum-put-get-derivation
1 parent 23623fe commit 4127bc8

File tree

9 files changed

+149
-4
lines changed

9 files changed

+149
-4
lines changed

modules/core/src/main/scala-3/doobie/util/GetPlatform.scala

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,23 @@
44

55
package doobie.util
66

7-
trait GetPlatform {}
7+
import scala.deriving.Mirror
8+
import scala.compiletime.constValue
9+
import scala.reflect.Enum
10+
11+
trait GetPlatform {
12+
private def of[A](name: String, cases: List[A], labels: List[String]): Get[A] =
13+
Get[String].temap { caseName =>
14+
labels.indexOf(caseName) match {
15+
case -1 => Left(s"enum $name does not contain case: $caseName")
16+
case i => Right(cases(i))
17+
}
18+
}
19+
20+
inline final def deriveEnumString[A <: Enum](using mirror: Mirror.SumOf[A]): Get[A] =
21+
of(
22+
constValue[mirror.MirroredLabel],
23+
summonSingletonCases[mirror.MirroredElemTypes, A](constValue[mirror.MirroredLabel]),
24+
summonLabels[mirror.MirroredElemLabels]
25+
)
26+
}

modules/core/src/main/scala-3/doobie/util/PutPlatform.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,14 @@
44

55
package doobie.util
66

7-
trait PutPlatform
7+
import scala.compiletime.constValue
8+
import scala.deriving.Mirror
9+
import scala.reflect.Enum
10+
11+
trait PutPlatform {
12+
inline final def deriveEnumString[A <: Enum](using mirror: Mirror.SumOf[A]): Put[A] =
13+
val _ = summonSingletonCases[mirror.MirroredElemTypes, A](constValue[mirror.MirroredLabel])
14+
val labels = summonLabels[mirror.MirroredElemLabels]
15+
16+
Put[String].contramap(a => labels(mirror.ordinal(a)))
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2013-2020 Rob Norris and Contributors
2+
// This software is licensed under the MIT License (MIT).
3+
// For more information see LICENSE or https://opensource.org/licenses/MIT
4+
5+
package doobie.util
6+
7+
import scala.compiletime.{constValue, erasedValue, error, summonInline}
8+
import scala.deriving.Mirror
9+
10+
private[util] inline final def summonLabels[T <: Tuple]: List[String] =
11+
inline erasedValue[T] match
12+
case _: EmptyTuple => Nil
13+
case _: (t *: ts) => constValue[t].asInstanceOf[String] :: summonLabels[ts]
14+
15+
private[util] inline final def summonSingletonCases[T <: Tuple, A](inline typeName: String): List[A] =
16+
inline erasedValue[T] match
17+
case _: EmptyTuple => Nil
18+
case _: (h *: t) =>
19+
inline summonInline[Mirror.Of[h]] match
20+
case m: Mirror.Singleton => m.fromProduct(EmptyTuple).asInstanceOf[A] :: summonSingletonCases[t, A](typeName)
21+
case m: Mirror =>
22+
error("Enum " + typeName + " contains non singleton case " + constValue[m.MirroredLabel])
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright (c) 2013-2020 Rob Norris and Contributors
2+
// This software is licensed under the MIT License (MIT).
3+
// For more information see LICENSE or https://opensource.org/licenses/MIT
4+
5+
package doobie.util
6+
7+
trait GetSuitePlatform {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright (c) 2013-2020 Rob Norris and Contributors
2+
// This software is licensed under the MIT License (MIT).
3+
// For more information see LICENSE or https://opensource.org/licenses/MIT
4+
5+
package doobie.util
6+
7+
trait PutSuitePlatform {}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) 2013-2020 Rob Norris and Contributors
2+
// This software is licensed under the MIT License (MIT).
3+
// For more information see LICENSE or https://opensource.org/licenses/MIT
4+
5+
package doobie.util
6+
7+
import Predef.augmentString
8+
9+
trait GetSuitePlatform { self: munit.FunSuite =>
10+
11+
enum EnumContainsNonSinletonCase {
12+
case One
13+
case Two(i: Int)
14+
}
15+
16+
enum EnumWithOnlySingletonCases {
17+
case One, Two, Three
18+
}
19+
20+
sealed trait NotEnum
21+
22+
case object NotEnum1 extends NotEnum
23+
24+
test("Get should not be derived for enum with non singleton case") {
25+
val compileError = compileErrors("Get.deriveEnumString[EnumContainsNonSinletonCase]")
26+
27+
assert(compileError.contains("Enum EnumContainsNonSinletonCase contains non singleton case Two"))
28+
}
29+
30+
test("Get should be derived for enum with only singleton cases") {
31+
Get.deriveEnumString[EnumWithOnlySingletonCases]
32+
}
33+
34+
test("Get should not be derived for sealed trait") {
35+
val compileError = compileErrors("Get.deriveEnumString[NotEnum]")
36+
37+
assert(compileError.contains(
38+
"Type argument GetSuitePlatform.this.NotEnum does not conform to upper bound scala.reflect.Enum"))
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) 2013-2020 Rob Norris and Contributors
2+
// This software is licensed under the MIT License (MIT).
3+
// For more information see LICENSE or https://opensource.org/licenses/MIT
4+
5+
package doobie.util
6+
7+
import Predef.augmentString
8+
9+
trait PutSuitePlatform { self: munit.FunSuite =>
10+
11+
enum EnumContainsNonSinletonCase {
12+
case One
13+
case Two(i: Int)
14+
}
15+
16+
enum EnumWithOnlySingletonCases {
17+
case One, Two, Three
18+
}
19+
20+
sealed trait NotEnum
21+
22+
case object NotEnum1 extends NotEnum
23+
24+
test("Put should not be derived for enum with non singleton case") {
25+
val compileError = compileErrors("Put.deriveEnumString[EnumContainsNonSinletonCase]")
26+
27+
assert(compileError.contains("Enum EnumContainsNonSinletonCase contains non singleton case Two"))
28+
}
29+
30+
test("Put should be derived for enum with only singleton cases") {
31+
Put.deriveEnumString[EnumWithOnlySingletonCases]
32+
}
33+
34+
test("Put should not be derived for sealed trait") {
35+
val compileError = compileErrors("Put.deriveEnumString[NotEnum]")
36+
37+
assert(compileError.contains(
38+
"Type argument PutSuitePlatform.this.NotEnum does not conform to upper bound scala.reflect.Enum"))
39+
}
40+
}

modules/core/src/test/scala/doobie/util/GetSuite.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import doobie.enumerated.JdbcType
99
import doobie.testutils.VoidExtensions
1010
import doobie.util.transactor.Transactor
1111

12-
class GetSuite extends munit.CatsEffectSuite {
12+
class GetSuite extends munit.CatsEffectSuite with GetSuitePlatform {
1313

1414
case class X(x: Int)
1515
case class Q(x: String)

modules/core/src/test/scala/doobie/util/PutSuite.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import cats.effect.IO
88
import doobie.testutils.VoidExtensions
99
import doobie.util.transactor.Transactor
1010

11-
class PutSuite extends munit.FunSuite {
11+
class PutSuite extends munit.FunSuite with PutSuitePlatform {
1212
case class X(x: Int)
1313

1414
case class Q(x: String)

0 commit comments

Comments
 (0)