автоматически генерировать объект case для класса case - PullRequest
0 голосов
/ 26 июня 2018

Как сделать так, чтобы компилятор scala автоматически генерировал объект case?

// Pizza class
class Pizza (val crust_type: String)

// companion object
object Pizza {
    val crustType = "crust_type"
}

Желаемые свойства для объекта case

  • для каждого атрибута в case class создайте атрибут в case object
  • установите значение в каждом соответствующем объекте case в строковое представление имени атрибута и измените camelCase на snake_case для имени атрибута объекта, оставьте snake_case для значения атрибута объекта

1 Ответ

0 голосов
/ 27 июня 2018

Вы можете создать макрос аннотации

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

@compileTimeOnly("enable macro paradise to expand macro annotations")
class GenerateCompanion extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro GenerateCompanion.impl
}

object GenerateCompanion {
  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._

    annottees match {
      case (c@q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>

        val vals = paramss.flatten.map(p => {
          val name = p.name.toString
          q"val ${TermName(underscoreToCamel(name))}: String = $name"
        })

        q"""
          $c
          object ${tpname.toTermName} {..$vals}
        """
    }
  }

  def underscoreToCamel(name: String): String = "_([a-z\\d])".r.replaceAllIn(name, _.group(1).toUpperCase)
}

и используйте его

@GenerateCompanion
class Pizza(val crust_type: String)

Pizza.crustType //crust_type

Новый макрос:

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

@compileTimeOnly("enable macro paradise to expand macro annotations")
class GenerateCompanion extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro GenerateCompanion.impl
}

object GenerateCompanion {
  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._

    def vals(paramss: Seq[Seq[ValDef]]): Seq[ValDef] =
      paramss.flatten.map(p => {
        val name = p.name.toString
        q"val ${TermName(underscoreToCamel(name))}: String = $name"
      })

    annottees match {
      case (c@q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
        q"""
          $c
          object ${tpname.toTermName} {
            ..${vals(paramss)}
          }
          """

      case (c@q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") ::
        q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
        q"""
           $c
           $mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
            ..$body
            ..${vals(paramss)}
           }
          """
    }
  }

  def underscoreToCamel(name: String): String = "_([a-z\\d])".r.replaceAllIn(name, _.group(1).toUpperCase)
}

Использование:

@GenerateCompanion
class Pizza(val crust_type: String, val foo_foo: Int)

object Pizza {
  def bar: String = "bar"
}

Pizza.crustType //crust_type
Pizza.fooFoo //foo_foo
Pizza.bar //bar
...