使Scala类使用宏扩展特征/抽象类

时间:2019-10-05 18:10:03

标签: scala traits scala-macros scala-macro-paradise

问题:

我想使带注释的类成为具有scala宏的另一个类的子类 我所拥有的:

字段包装:

class Field(fieldType: DbModelFieldType, fieldName: String) 

抽象类(所有带注释的类的基类):

abstract class DatabaseModel {
  def fields: Seq[Fields]
}

我有一个案例课:

Model(num: Int, sym: Char, descr: String)

,如果用@GetFromDB注释该类

@GetFromDB
Model(num: Int, sym: Char, descr: String)
case class Model(num: Int, sym: Char, descr: String) extends DatabaseModel {
   override def fields: Seq[Fields] = 
       Seq(Field(IntFieldType(), "num"),
           Field(CharFieldType(), "sym"),
           Field(StringFieldType(), "descr")
          ) 
}

我想要的结果应该是这样的:

val m: DatabaseModel = Model(1, 'A', "First Name")

我看过类似的问题

Generate companion object for case class with methods (field = method)

那么我该如何扩展该解决方案以获得所需的结果?

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

object Macros {
  @compileTimeOnly("enable macro paradise")
  class GenerateCompanionWithFields extends StaticAnnotation {
    def macroTransform(annottees: Any*): Any = macro Macro.impl
  }

  object Macro {
    def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
      import c.universe._
      annottees match {
        case (cls @ q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>

          val newMethods = paramss.flatten.map {
            case q"$_ val $tname: $tpt = $_" =>
              q"def $tname(): String = ${tpt.toString}"
          }

          q"""
             $cls

             object ${tpname.toTermName} {
               ..$newMethods
             }
           """
      }
    }
  }
}

1 个答案:

答案 0 :(得分:1)

尝试

"GroupAggregate  (cost=8.18..8.21 rows=1 width=604)"
"  Group Key: country, text"
"  ->  Sort  (cost=8.18..8.18 rows=1 width=556)"
"        Sort Key: country, text"
"        ->  Index Scan using idx_org_date on report  (cost=0.14..8.17 rows=1 width=556)"
"              Index Cond: ((org_id = 1) AND (date >= '2019-01-01'::date) AND (date <= '2019-02-02'::date))"