Skip to content

Instantly share code, notes, and snippets.

@EtaCassiopeia
Created October 25, 2023 16:49
Show Gist options
  • Save EtaCassiopeia/375bf17e94e2a9ff05990a2d76b29fce to your computer and use it in GitHub Desktop.
Save EtaCassiopeia/375bf17e94e2a9ff05990a2d76b29fce to your computer and use it in GitHub Desktop.
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