private def toGenericRecordInternal()

in scio-avro/src/main/scala/com/spotify/scio/avro/types/ConverterProvider.scala [127:210]


  private def toGenericRecordInternal(c: blackbox.Context)(tpe: c.Type): c.Tree = {
    import c.universe._

    // =======================================================================
    // Converter helpers
    // =======================================================================

    def cast(tree: Tree, tpe: Type): Tree =
      tpe match {
        case t if t =:= typeOf[Boolean] => tree
        case t if t =:= typeOf[Int]     => tree
        case t if t =:= typeOf[Long]    => tree
        case t if t =:= typeOf[Float]   => tree
        case t if t =:= typeOf[Double]  => tree
        case t if t =:= typeOf[String]  => tree

        case t if t =:= typeOf[ByteString] => q"$tree.asReadOnlyByteBuffer"
        case t if t =:= typeOf[Array[Byte]] =>
          q"_root_.java.nio.ByteBuffer.wrap($tree)"

        case t if t.erasure <:< typeOf[scala.collection.Map[String, _]].erasure =>
          map(tree, tpe.typeArgs.tail.head)

        case t if t.erasure <:< typeOf[List[_]].erasure =>
          list(tree, tpe.typeArgs.head)

        case t if isCaseClass(c)(t) =>
          val fn = TermName("r" + t.typeSymbol.name)
          q"""{
                val $fn = $tree
                ${constructor(t, fn)}
              }
          """
        case _ => c.abort(c.enclosingPosition, s"Unsupported type: $tpe")
      }

    def option(tree: Tree, tpe: Type): Tree =
      q"if ($tree.isDefined) ${cast(q"$tree.get", tpe)} else null"

    def list(tree: Tree, tpe: Type): Tree =
      q"asJava($tree.map(x => ${cast(q"x", tpe)}))"

    def map(tree: Tree, tpe: Type): Tree =
      q"asJava($tree.iterator.map(kv => kv._1 -> ${cast(q"kv._2", tpe)}).toMap)"

    def field(symbol: Symbol, fn: TermName): (String, Tree) = {
      val name = symbol.name.toString
      val fieldName = symbol.name.toString
      val tpe = symbol.asMethod.returnType

      val tree = q"$fn.${TermName(name)}"
      if (tpe.erasure =:= typeOf[Option[_]].erasure) {
        (fieldName, option(tree, tpe.typeArgs.head))
      } else {
        (fieldName, cast(tree, tpe))
      }
    }

    def constructor(tpe: Type, fn: TermName): Tree = {
      val sets = tpe.erasure match {
        case t if isCaseClass(c)(t) => getFields(c)(t).map(s => field(s, fn))
        case _                      => c.abort(c.enclosingPosition, s"Unsupported type: $tpe")
      }
      val schemaOf = q"${p(c, ScioAvroType)}.schemaOf[$tpe]"
      val header =
        q"val result = new ${p(c, ApacheAvro)}.generic.GenericData.Record($schemaOf)"
      val body = sets.map { case (fieldName, value) =>
        q"if (${p(c, ScioAvro)}.types.ConverterUtil.notNull($value)) result.put($fieldName, $value)"
      }
      val footer = q"result"
      q"{$header; ..$body; $footer}"
    }

    // =======================================================================
    // Entry point
    // =======================================================================

    val tn = TermName("r")
    q"""(r: $tpe) => {
          import _root_.scala.jdk.javaapi.CollectionConverters._
          ${constructor(tpe, tn)}
        }
    """
  }