I have a sealed trait BotUpdate and case classes which implement it. I created a macro annotation which adds a method for each BotUpdate implementation and a method def onUpdate(upd: BotUpdate): Unit which calls one of methods generated for updates, based on upd type.
package im.actor.bot.macros
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.language.postfixOps
import scala.reflect.macros.blackbox.Context
final class BotInterface extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro BotInterfaceImpl.impl
}
private object BotInterfaceImpl {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val updates = c.mirror.staticClass("im.actor.bot.BotMessages.BotUpdate").knownDirectSubclasses collect {
case cs: ClassSymbol ⇒ cs
}
val (updCallbacks, updDefs) = updates map { decl ⇒
val methodName = s"on${decl.name.decodedName.toString}"
val method = TermName(methodName)
((decl, method), q"def $method(upd: $decl): Unit")
} unzip
val updCases = updCallbacks map {
case (upd, method) ⇒
cq"""
u: $upd => $method(u)
"""
}
val onUpdateDef =
q"""def onUpdate(upd: im.actor.bot.BotMessages.BotUpdate): Unit = {
upd match {
case ..$updCases
}
}"""
annottees map (_.tree) toList match {
case q"$mods trait $traitName extends ..$parents { ..$body }" :: Nil ⇒
c.Expr[Any](
q"""
$mods trait $traitName extends ..$parents {
..$updDefs
$onUpdateDef
..$body
}
"""
)
case q"class $className(..$args) extends ..$parents { ..$body }" :: Nil ⇒
c.Expr[Any](
q"""
class $className(..$args) extends ..$parents {
..$updDefs
$onUpdateDef
..$body
}
"""
)
case unexpected ⇒ c.abort(c.enclosingPosition, "Invalid annottee")
}
}
}
and use it
package im.actor.bot
import im.actor.bot.macros.BotInterface
@BotInterface
trait BotBase
The problem is that when I call onUpdate method in a final class which implements BotBase, I get AbstractMethodError. It definitely implements all on* methods generated for updates.
java.lang.AbstractMethodError: im.actor.server.bot.InternalBot.onUpdate(Lim/actor/bot/BotMessages$BotUpdate;)
P.S. Another question is, you see this anti-DRY pattern matching in annottees map? Is it possible to make it DRY while using quasi-quotes for extracting type body and parents using?
Aucun commentaire:
Enregistrer un commentaire