Created
October 25, 2023 16:49
-
-
Save EtaCassiopeia/375bf17e94e2a9ff05990a2d76b29fce to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.ncl.vacation.analytics.utils | |
import scala.language.experimental.macros | |
import scala.reflect.macros.blackbox | |
import scala.reflect.runtime.universe.typeOf | |
object ExtendCaseClass { | |
def extendCaseClassImpl[T: c.WeakTypeTag](c: blackbox.Context)(additionalFields: c.Expr[Any]*): c.Expr[Any] = { | |
import c.universe._ | |
// Extract the name of the base class | |
val baseClassName = weakTypeOf[T].typeSymbol.name.toString | |
// Extract fields | |
val fields = weakTypeOf[T].decls.collectFirst { | |
case m: MethodSymbol if m.isPrimaryConstructor => m.paramLists.headOption.getOrElse { | |
c.abort(c.enclosingPosition, s"$baseClassName does not have a primary constructor with parameters.") | |
} | |
}.getOrElse { | |
c.abort(c.enclosingPosition, s"$baseClassName does not have a primary constructor.") | |
} | |
val additionalFieldTrees = additionalFields.map(_.tree) | |
// Generate field names and types from the original case class | |
val originalFields = fields.map { sym => | |
q"val ${sym.name.toTermName}: ${sym.typeSignature}" | |
} | |
// Use the additional fields to create new field definitions | |
// This assumes additional fields are passed as tuples of (String, Type) | |
val newFields = additionalFieldTrees.map { | |
case q"(${Literal(Constant(name: String))}, $tpe)" => | |
q"val ${TermName(name)}: $tpe" | |
} | |
val allFields = originalFields ++ newFields | |
// Create a new case class with the desired name format | |
val result = q""" | |
case class ${TypeName(s"Extended$baseClassName")}(..$allFields) | |
""" | |
c.Expr(result) | |
} | |
def extendCaseClass[T](additionalFields: Any*): Any = macro extendCaseClassImpl[T] | |
} | |
/* | |
lazy val macroProject = project | |
.in(file("macro")) | |
.settings( | |
name := "macro-project", | |
libraryDependencies ++= Seq( | |
"org.scala-lang" % "scala-reflect" % scalaVersion.value | |
) | |
) | |
lazy val mainProject = project | |
.in(file(".")) | |
.dependsOn(macroProject) | |
.settings( | |
name := "main-project" | |
) | |
case class Person(name: String, age: Int) | |
extendCaseClass[Person](("address", typeOf[String]), ("phone", typeOf[Int])) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment