Last active
February 6, 2020 09:10
-
-
Save abhandaru/b540f8f917b476b1b5ef7267c262e1d2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import language.experimental.macros | |
import scala.reflect.macros.whitebox.Context | |
// Macro for iterating sealed trait enums | |
// https://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala | |
object SealedEnum { | |
def values[A]: Set[A] = macro values_impl[A] | |
def values_impl[A: c.WeakTypeTag](c: Context) = { | |
import c.universe._ | |
val symbol = weakTypeOf[A].typeSymbol | |
if (!symbol.isClass) c.abort( | |
c.enclosingPosition, | |
"Can only enumerate values of a sealed trait or class." | |
) else if (!symbol.asClass.isSealed) c.abort( | |
c.enclosingPosition, | |
"Can only enumerate values of a sealed trait or class." | |
) else { | |
val tpe = symbol.asClass | |
tpe.typeSignature // needed because of scala core bug: https://github.com/scala/bug/issues/7588 | |
val children = tpe.knownDirectSubclasses.toList | |
if (!children.forall(_.isModuleClass)) c.abort( | |
c.enclosingPosition, | |
"All children must be objects." | |
) else c.Expr[Set[A]] { | |
def sourceModuleRef(sym: Symbol) = Ident( | |
sym.asInstanceOf[ | |
scala.reflect.internal.Symbols#Symbol | |
].sourceModule.asInstanceOf[Symbol] | |
) | |
Apply( | |
Select( | |
reify(Set).tree, | |
TermName("apply") | |
), | |
children.map(sourceModuleRef(_)) | |
) | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.scalatest.{Matchers, WordSpec} | |
sealed class TestSealedClass(key: String) | |
object TestSealedClass { | |
object ClassA extends TestSealedClass("A") | |
object ClassB extends TestSealedClass("B") | |
object ClassC extends TestSealedClass("C") | |
} | |
sealed trait TestSealedTrait | |
object TestSealedTrait { | |
object ClassA extends TestSealedTrait | |
object ClassB extends TestSealedTrait | |
object ClassC extends TestSealedTrait | |
} | |
class SealedEnumSpec extends WordSpec with Matchers { | |
"SealedEnum" should { | |
"enumerate sealed class correctly" in { | |
val values = SealedEnum.values[TestSealedClass] | |
values should be(Set(TestSealedClass.ClassA, TestSealedClass.ClassB, TestSealedClass.ClassC)) | |
} | |
"enumerate sealed trait correctly" in { | |
val values = SealedEnum.values[TestSealedTrait] | |
values should be(Set(TestSealedTrait.ClassA, TestSealedTrait.ClassB, TestSealedTrait.ClassC)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment