automatically generate case object for case class

后端 未结 1 445
悲&欢浪女
悲&欢浪女 2021-01-22 06:52

How can I have the scala compiler automatically generate the case object?

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

// companion object
object Pizza {         


        
1条回答
  •  温柔的废话
    2021-01-22 07:41

    You can create macro annotation

    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)
    }
    

    and use it

    @GenerateCompanion
    class Pizza(val crust_type: String)
    
    Pizza.crustType //crust_type
    

    New macro:

    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)
    }
    

    Usage:

    @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
    

    0 讨论(0)
提交回复
热议问题