Skip to content

Instantly share code, notes, and snippets.

@tototoshi
Last active March 5, 2017 14:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tototoshi/ac9de2c2d683b64c3ac0e59c660f2710 to your computer and use it in GitHub Desktop.
Save tototoshi/ac9de2c2d683b64c3ac0e59c660f2710 to your computer and use it in GitHub Desktop.
@tostring and @logger macro annotation
package com.example
import java.time.LocalDateTime
@ToString
case class UserName(
firs: String,
last: String
)
@ToString
case class User(
id: Int,
name: UserName,
country: String,
createdAt: LocalDateTime
)
@Logger
object Main {
def main(args: Array[String]): Unit = {
val user = User(1, UserName("Toshiyuki", "Takahashi"), "Japan", LocalDateTime.now())
logger.info(user.toString)
// output
// 23:44:35.499 [run-main-3a] INFO com.example.Main$ - User(id=1, name=UserName(firs=Toshiyuki, last=Takahashi), country=Japan, createdAt=2017-03-05T23:44:35.498)
}
}
package com.example
import scala.collection.immutable.Seq
import scala.meta._
class ToString extends scala.annotation.StaticAnnotation {
inline def apply(defn: Any): Any = meta {
def addStat(classDefn: Defn.Class, stat: Stat): Defn.Class = {
val templ = classDefn.templ
val stats = templ.stats.getOrElse(Seq.empty[Stat])
val newStats = stats :+ stat
val newTempl = templ.copy(stats = Some(newStats))
classDefn.copy(templ = newTempl)
}
def addToStringMethod(classDefn: Defn.Class, objDefn: Defn.Object): Stat = {
val className = classDefn.name.toString
val paramNames = classDefn.ctor.paramss.flatten.map { param =>
val fieldName = param.name.toString
q"""${Lit(fieldName)} + "=" + ${Term.Name(fieldName)}"""
}
val body = paramNames.reduce { (a, b) => q"""$a + ", " + $b""" }
val method = q"""override def toString(): String = ${Lit(className)} + "(" + $body + ")""""
val classDefnWithToString = addStat(classDefn, method)
Term.Block(scala.collection.immutable.Seq(classDefnWithToString, objDefn))
}
defn match {
case Term.Block(Seq(classDefn: Defn.Class, objDefn: Defn.Object)) =>
addToStringMethod(classDefn, objDefn)
case classDefn: Defn.Class =>
val objName = Term.Name(classDefn.name.toString)
val objDefn = q"object $objName {}"
addToStringMethod(classDefn, objDefn)
case _ => sys.error("error")
}
}
}
package com.example
import scala.annotation.StaticAnnotation
import scala.meta._
import scala.collection.immutable.Seq
class Logger extends StaticAnnotation {
inline def apply(defn: Any): Any = meta {
def addStatToTempl(templ: Template, stat: Stat): Template = {
val stats = templ.stats.getOrElse(Seq.empty[Stat])
val newStats = stat +: stats
templ.copy(stats = Some(newStats))
}
def addStatToClassDefn(classDefn: Defn.Class, stat: Stat): Defn.Class = {
val templ = classDefn.templ
val newTempl = addStatToTempl(templ, stat)
classDefn.copy(templ = newTempl)
}
def addStatToObjDefn(objDefn: Defn.Object, stat: Stat): Defn.Object = {
val templ = objDefn.templ
val newTempl = addStatToTempl(templ, stat)
objDefn.copy(templ = newTempl)
}
def loggerMethod: Stat = {
q"""
private val logger = {
import org.slf4j.LoggerFactory
LoggerFactory.getLogger(getClass)
}
"""
}
defn match {
case objDefn: Defn.Object =>
addStatToObjDefn(objDefn, loggerMethod)
case classDefn: Defn.Class =>
addStatToClassDefn(classDefn, loggerMethod)
case Term.Block(Seq(classDefn: Defn.Class, objDefn: Defn.Object)) =>
Term.Block(
Seq(
addStatToClassDefn(classDefn, loggerMethod),
addStatToObjDefn(objDefn, loggerMethod)
)
)
case _ => sys.error("error")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment