Skip to content

Instantly share code, notes, and snippets.

@caeus
Last active September 30, 2020 12:49
Show Gist options
  • Save caeus/608d8656df4592c849506bbb5e7337ae to your computer and use it in GitHub Desktop.
Save caeus/608d8656df4592c849506bbb5e7337ae to your computer and use it in GitHub Desktop.
IsEnum Typeclass (to be honest, better than Enumaratum)
itrait UEnum[T] {
def values: Map[String, T]
def name(t: T): String
def value(name: String): Option[T]
}
object UEnum { obj =>
final case class UEnumImpl[T](seq: T*) extends UEnum[T] {
val values: Map[String, T] = seq.map(s => s.toString -> s).toMap
override def name(t: T): String =
if (seq contains t) t.toString else ""
override def value(name: String): Option[T] = values.get(name)
}
@inline
def apply[T](implicit ie: UEnum[T]): UEnum[T] = ie
@SuppressWarnings(Array("org.wartremover.warts.All"))
def impl[A: c.WeakTypeTag](c: whitebox.Context): c.Expr[UEnum[A]] = {
def error(message: String) = c.abort(c.enclosingPosition, s"UEnum: $message")
def ensure(cond: Boolean, msg: String): Unit = if (!cond) error(msg)
import c.universe._
val aType = weakTypeOf[A].typeSymbol.asClass
val uEnumType = weakTypeOf[UEnum[A]].typeSymbol
val uEnumAType = tq"$uEnumType[$aType]"
ensure(aType.isSealed, "Only works on sealed traits")
def subClassesOf(typeS: ClassSymbol): List[Symbol] = {
typeS.knownDirectSubclasses.toList.flatMap { sub =>
val clazz = sub.asClass
if (clazz.isAbstract) {
subClassesOf(clazz)
} else {
ensure(clazz.isCaseClass && clazz.isModuleClass, "Subtypes need to be case objects only")
List(sub)
}
}
}
val caseObjectClassSymbols: List[Symbol] = subClassesOf(aType)
val caseObjectRefs = caseObjectClassSymbols.map { s =>
//TODO improve this ugly hack (but how?)
Ident(s.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol])
//Ident(s.asType.companion)
}
val uEnumImplCompanion = Ident(weakTypeOf[UEnumImpl[A]].typeSymbol.companion)
c.Expr[UEnum[A]](q"""
$uEnumImplCompanion(..$caseObjectRefs): $uEnumAType
""")
}
def gen[T]: UEnum[T] = macro impl[T]
// scalastyle:off object.name
object auto {
implicit def gen[T]: UEnum[T] = macro impl[T]
}
// scalastyle:off object.name
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment