Created
November 26, 2016 23:48
-
-
Save reimai/2ac1172b78c0190f32b7e9f2c5cf1c12 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
package rei | |
import scala.language.experimental.macros | |
import scala.reflect.macros.blackbox | |
object Macros { | |
def ?[T](s: T): Option[T] = macro opt_impl[T] | |
def opt[T](s: T): Option[T] = macro opt_impl[T] | |
def opt_impl[T](c: blackbox.Context)(s: c.Expr[T]): c.Expr[Option[T]] = { | |
import c.universe._ | |
def fail(message: String): Nothing = c.abort(c.enclosingPosition, message) | |
val usage = | |
""" | |
Hi, this is a macro for accessing protobuf option fields w/o manually writing "enrichers" | |
this is how to use it: | |
val optUser: Option[User] = opt(offer.getUser()) | |
it will wrap the dratted thing with an Option using protoc's "has" method | |
because defaults are not nulls and we'd like to distinguish 'em | |
""".stripMargin | |
def check(that: Boolean, msg: String): Unit = if (!that) fail(s"$usage\n\nProblem: $msg") | |
check(s.tree.symbol.isMethod, s"${s.tree.symbol} is not a method") | |
//somehow calling 'isGetter' returns false, may be because it's java | |
check(s.tree.symbol.owner.isClass, s"Are you sure ${s.tree.symbol.owner} is a protobuf generated class?") | |
val get = s.tree.symbol.asMethod | |
val item = get.name.toString.substring(3) | |
val clazz = s.tree.symbol.owner.asClass | |
val maybeHas = clazz.toType.members | |
.find(s => s.isMethod && s.asMethod.name.toString == s"has$item") | |
check(maybeHas.isDefined, | |
s"There is no has$item method in class ${clazz.name.toString}, " + | |
s"are you sure ${item.toLowerCase} is a protobuf optional field?") | |
val has = maybeHas.get.asMethod | |
s.tree match { | |
case q"$obj.$get()" => | |
c.Expr( | |
q""" | |
if ($obj.$has()) | |
Some($s) | |
else | |
None | |
""") | |
case otherwise => fail(s"Something went wrong, tree: $otherwise") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment