json.scala 2.6 KB
Newer Older
梦境迷离's avatar
梦境迷离 已提交
1 2 3 4 5 6 7
package io.github.dreamylost

import scala.annotation.{ StaticAnnotation, compileTimeOnly }
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

/**
8
 * annotation to generate play-json implicit object for case classes.
梦境迷离's avatar
梦境迷离 已提交
9 10 11 12 13 14 15 16 17 18
 *
 * @author 梦境迷离
 * @since 2021/6/13
 * @version 1.0
 */
@compileTimeOnly("enable macro to expand macro annotations")
final class json extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro jsonMacro.impl
}

19
object jsonMacro extends MacroCommon {
梦境迷离's avatar
梦境迷离 已提交
20 21 22 23 24 25 26 27 28 29 30 31
  def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    def jsonFormatter(className: TypeName, fields: List[Tree]): c.universe.Tree = {
      fields.length match {
        case 0 => c.abort(c.enclosingPosition, "Cannot create json formatter for case class with no fields")
        case _ =>
          c.info(c.enclosingPosition, s"jsonFormatter className: $className, field length: ${fields.length}", force = true)
          q"implicit val jsonAnnotationFormat = play.api.libs.json.Json.format[$className]"
      }
    }

32 33
    // The dependent type need aux-pattern in scala2. Now let's get around this.
    def modifiedDeclaration(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None): Any = {
梦境迷离's avatar
梦境迷离 已提交
34
      val (className, fields) = classDecl match {
35
        case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends ..$bases { ..$body }" =>
梦境迷离's avatar
梦境迷离 已提交
36 37 38
          if (!mods.asInstanceOf[Modifiers].hasFlag(Flag.CASE)) {
            c.abort(c.enclosingPosition, s"Annotation is only supported on case class. classDef: $classDecl, mods: $mods")
          } else {
39 40
            c.info(c.enclosingPosition, s"modifiedDeclaration className: $tpname, paramss: $paramss", force = true)
            (tpname, paramss)
梦境迷离's avatar
梦境迷离 已提交
41 42 43 44
          }
        case _ => c.abort(c.enclosingPosition, s"Annotation is only supported on case class. classDef: $classDecl")
      }
      c.info(c.enclosingPosition, s"modifiedDeclaration className: $className, fields: $fields", force = true)
45 46 47 48 49 50 51 52 53
      val cName = className match {
        case t: TypeName => t
      }
      val format = jsonFormatter(cName, fields.asInstanceOf[List[List[Tree]]].flatten)
      val compDecl = modifiedCompanion(c)(compDeclOpt, format, cName)
      c.info(c.enclosingPosition, s"format: $format, compDecl: $compDecl", force = true)
      // Return both the class and companion object declarations
      c.Expr(
        q"""
梦境迷离's avatar
梦境迷离 已提交
54 55 56 57 58 59
        $classDecl
        $compDecl
      """)

    }

60 61 62
    c.info(c.enclosingPosition, s"json annottees: $annottees", force = true)
    val resTree = handleWithImplType(c)(annottees: _*)(modifiedDeclaration)
    printTree(c)(force = true, resTree.tree)
梦境迷离's avatar
梦境迷离 已提交
63

64
    resTree
梦境迷离's avatar
梦境迷离 已提交
65 66
  }
}