Skip to content

Commit

Permalink
Reimplementing enum macros for Scala 3. Based on pureconfig's which a…
Browse files Browse the repository at this point in the history
…re more lightweight. (#207)
  • Loading branch information
luksow committed May 17, 2022
1 parent d610bbe commit 57acb2a
Showing 1 changed file with 25 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package pl.iterators.kebs.macros.enums

import scala.quoted._
import scala.compiletime.{constValue, erasedValue, error, summonInline}
import scala.deriving.Mirror
import scala.reflect.{ClassTag, Enum}

trait EnumLike[T] {
def values: Array[T]
Expand All @@ -11,19 +14,29 @@ trait EnumLike[T] {
class EnumOf[E](val `enum`: EnumLike[E])

object EnumOf {
inline given [E]: EnumOf[E] = ${EnumOf.impl[E]()}
inline given [E <: Enum](using m: Mirror.SumOf[E], ct: ClassTag[E]): EnumOf[E] = {
val enumValues = summonCases[m.MirroredElemTypes, E]
EnumOf[E](new EnumLike[E] {
override def values: Array[E] = enumValues.toArray
override def valueOf(name: String): E = enumValues.find(_.toString == name).getOrElse(throw new IllegalArgumentException(s"enum case not found: $name"))
override def fromOrdinal(ordinal: Int): E = enumValues.lift(ordinal).getOrElse(throw new NoSuchElementException(ordinal.toString))
})
}

private def impl[T]()(using Quotes, Type[T]): Expr[EnumOf[T]] = {
import quotes.reflect._
val companion = Ref(TypeRepr.of[T].typeSymbol.companionModule)
'{
new EnumOf(
new EnumLike[T] {
def values: Array[T] = ${Select.unique(companion, "values").asExprOf[Array[T]]}
def valueOf(name: String): T = ${Apply(Select.unique(companion, "valueOf"), List('{name}.asTerm)).asExprOf[T]}
def fromOrdinal(ordinal: Int): T = ${Apply(Select.unique(companion, "fromOrdinal"), List('{ordinal}.asTerm)).asExprOf[T]}
}
)
inline private def widen[A, B](a: A): A & B =
inline a match {
case b: B => b
}

inline private def summonCases[T <: Tuple, A]: List[A] =
inline erasedValue[T] match {
case _: (h *: t) =>
(inline summonInline[Mirror.Of[h]] match {
case m: Mirror.Singleton =>
widen[m.MirroredMonoType, A](m.fromProduct(EmptyTuple)) :: summonCases[t, A]
case x => error("Enums cannot include parameterized cases.")
})

case _: EmptyTuple => Nil
}
}

0 comments on commit 57acb2a

Please sign in to comment.