I'm trying to write a simple macro to pull public fields from a class, along with their 'schema' (for Spark SQL). Now, a schema
is simply a string name, and a spark sql DataType
(StringType
, BooleanType
for example). I'm pattern matching to convert from a Scala type to a spark sql type. BUT, I can't seem to get the local variable (dt
, a spark sql DataType
) into my tree.
Weird thing is, if I replace dt
with the literal StringType
, it works perfectly. If it can do that, I'm not sure why it can't take it from a variable. Even doing val dt = StringType
doesn't work.
I feel like there is some way of doing something like c.Expr[DataType](Select(<local variable>).splice()
but I can't figure it out.
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
import org.apache.spark.sql.types.{DataType, StringType, BooleanType}
object DataExtractor {
case class SomeField(name: StructField, value: Any)
type SomeData = Seq[SomeField]
implicit class DataExtraction[M <: DataExtractor](val model: M) extends AnyVal {
def allData: SomeData = macro Macros.impl[M]
}
private object Macros {
def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[SomeData] = {
import c.universe._
val applier = Select(reify(Seq).tree, TermName("apply"))
val model = Select(c.prefix.tree, TermName("model"))
val terms = weakTypeOf[T].members.collect {
case m: MethodSymbol if m.isGetter && m.isPublic => m
}.map( t => {
val name = t.name.decodedName.toString
val value = c.Expr(Select(model, t.name))
val sig: Type = t.typeSignature.resultType
val dt: DataType = sig match {
case a if sig =:= typeOf[String] => StringType
case e if sig =:= typeOf[Boolean] => BooleanType
case _ => throw new Exception(s"Failed to match a ${sig.toString}")
}
reify(ClaimField(
StructField(c.Expr[String](Literal(Constant(name))).splice, dt),
value.splice
)).tree
})
c.Expr[SomeData](Apply(applier, terms.toList))
}
}
}
Aucun commentaire:
Enregistrer un commentaire