in scio-google-cloud-platform/src/main/scala/com/spotify/scio/bigquery/types/ConverterProvider.scala [266:334]
private def fromTableRowInternal(c: blackbox.Context)(tpe: c.Type): c.Tree = {
import c.universe._
val ops = q"_root_.com.spotify.scio.bigquery.syntax.TableRowOps"
val bs = q"_root_.com.google.protobuf.ByteString"
// =======================================================================
// Converter helpers
// =======================================================================
def cast(tree: Tree, tpe: Type): Tree = {
val provider: OverrideTypeProvider = OverrideTypeProviderFinder.getProvider
tpe match {
case t if provider.shouldOverrideType(c)(t) => provider.createInstance(c)(t, q"$tree")
case t if t =:= typeOf[Boolean] => q"$ops.boolean($tree)"
case t if t =:= typeOf[Int] => q"$ops.int($tree)"
case t if t =:= typeOf[Long] => q"$ops.long($tree)"
case t if t =:= typeOf[Float] => q"$ops.float($tree)"
case t if t =:= typeOf[Double] => q"$ops.double($tree)"
case t if t =:= typeOf[String] => q"$ops.string($tree)"
case t if t =:= typeOf[BigDecimal] => q"$ops.numeric($tree)"
case t if t =:= typeOf[Array[Byte]] => q"$ops.bytes($tree)"
case t if t =:= typeOf[ByteString] => q"$bs.copyFrom($ops.bytes($tree))"
case t if t =:= typeOf[Instant] => q"$ops.timestamp($tree)"
case t if t =:= typeOf[LocalDate] => q"$ops.date($tree)"
case t if t =:= typeOf[LocalTime] => q"$ops.time($tree)"
case t if t =:= typeOf[LocalDateTime] => q"$ops.datetime($tree)"
case t if t =:= typeOf[Geography] => q"$ops.geography($tree)"
case t if t =:= typeOf[Json] => q"$ops.json($tree)"
case t if t =:= typeOf[BigNumeric] => q"$ops.bignumeric($tree)"
case t if isCaseClass(c)(t) =>
// nested records
val r = TermName("r" + t.typeSymbol.name)
q"""{
val $r = $ops.record($tree)
${constructor(t, r)}
}"""
case _ => c.abort(c.enclosingPosition, s"Unsupported type: $tpe")
}
}
def field(symbol: Symbol, row: TermName): Tree = {
val name = symbol.name.toString
val tpe = symbol.asMethod.returnType
tpe match {
case t if t.erasure =:= typeOf[Option[_]].erasure =>
q"$ops.nullable($name)($row).map(x => ${cast(q"x", t.typeArgs.head)})"
case t if t.erasure =:= typeOf[List[_]].erasure =>
q"$ops.repeated($name)($row).map(x => ${cast(q"x", t.typeArgs.head)})"
case t =>
q"${cast(q"$ops.required($name)($row)", t)}"
}
}
def constructor(tpe: Type, row: TermName): Tree = {
val companion = tpe.typeSymbol.companion
val gets = tpe.erasure match {
case t if isCaseClass(c)(t) => getFields(c)(t).map(s => field(s, row))
case _ => c.abort(c.enclosingPosition, s"Unsupported type: $tpe")
}
q"$companion(..$gets)"
}
// =======================================================================
// Entry point
// =======================================================================
val r = TermName("r")
q"($r: ${typeOf[TableRow]}) => ${constructor(tpe, r)}"
}