def toFieldsImpl[T]()

in scalding-core/src/main/scala/com/twitter/scalding/macros/impl/FieldsProviderImpl.scala [58:177]


  def toFieldsImpl[T](c: Context)(implicit T: c.WeakTypeTag[T]): c.Expr[cascading.tuple.Fields] =
    toFieldsCommonImpl(c, NamedWithPrefix, false)(T)

  def toFieldsWithUnknownImpl[T](c: Context)(implicit T: c.WeakTypeTag[T]): c.Expr[cascading.tuple.Fields] =
    toFieldsCommonImpl(c, NamedWithPrefix, true)(T)

  def toFieldsWithUnknownNoPrefixImpl[T](c: Context)(implicit
      T: c.WeakTypeTag[T]
  ): c.Expr[cascading.tuple.Fields] =
    toFieldsCommonImpl(c, NamedNoPrefix, true)(T)

  def toIndexedFieldsImpl[T](c: Context)(implicit T: c.WeakTypeTag[T]): c.Expr[cascading.tuple.Fields] =
    toFieldsCommonImpl(c, Indexed, false)(T)

  def toIndexedFieldsWithUnknownImpl[T](c: Context)(implicit
      T: c.WeakTypeTag[T]
  ): c.Expr[cascading.tuple.Fields] =
    toFieldsCommonImpl(c, Indexed, true)(T)

  def toFieldsCommonImpl[T](c: Context, namingScheme: NamingScheme, allowUnknownTypes: Boolean)(implicit
      T: c.WeakTypeTag[T]
  ): c.Expr[cascading.tuple.Fields] = {
    import c.universe._

    import TypeDescriptorProviderImpl.optionInner

    @tailrec
    def isNumbered(t: Type): Boolean =
      t match {
        case tpe if tpe =:= typeOf[Boolean] => true
        case tpe if tpe =:= typeOf[Short]   => true
        case tpe if tpe =:= typeOf[Int]     => true
        case tpe if tpe =:= typeOf[Long]    => true
        case tpe if tpe =:= typeOf[Float]   => true
        case tpe if tpe =:= typeOf[Double]  => true
        case tpe if tpe =:= typeOf[String]  => true
        case tpe =>
          optionInner(c)(tpe) match { // linter:disable:UseOptionExistsNotPatMatch
            case Some(t) =>
              // we need this match style to do tailrec
              isNumbered(t)
            case None => false
          }
      }

    object FieldBuilder {
      // This is method on the object to work around this compiler bug: SI-6231
      def toFieldsTree(fb: FieldBuilder, scheme: NamingScheme): Tree = {
        val nameTree = scheme match {
          case Indexed =>
            val indices = fb.names.zipWithIndex.map(_._2)
            q"""_root_.scala.Array.apply[_root_.java.lang.Comparable[_]](..$indices)"""
          case _ =>
            q"""_root_.scala.Array.apply[_root_.java.lang.Comparable[_]](..${fb.names})"""
        }
        q"""new _root_.cascading.tuple.Fields($nameTree,
          _root_.scala.Array.apply[_root_.java.lang.reflect.Type](..${fb.columnTypes}))
         """
      }
    }
    sealed trait FieldBuilder {
      def columnTypes: Vector[Tree]
      def names: Vector[String]
    }
    final case class Primitive(name: String, tpe: Type) extends FieldBuilder {
      def columnTypes = Vector(q"""_root_.scala.Predef.classOf[$tpe]""")
      def names = Vector(name)
    }
    final case class OptionBuilder(of: FieldBuilder) extends FieldBuilder {
      // Options just use Object as the type, due to the way cascading works on number types
      def columnTypes = of.columnTypes.map(_ => q"""_root_.scala.Predef.classOf[_root_.java.lang.Object]""")
      def names = of.names
    }
    final case class CaseClassBuilder(prefix: String, members: Vector[FieldBuilder]) extends FieldBuilder {
      def columnTypes = members.flatMap(_.columnTypes)
      def names = for {
        member <- members
        name <- member.names
      } yield if (namingScheme == NamedWithPrefix && prefix.nonEmpty) s"$prefix.$name" else name
    }

    /**
     * This returns a List of pairs which flatten fieldType into (class, name) pairs
     */
    def matchField(fieldType: Type, name: String): FieldBuilder =
      fieldType match {
        case tpe if tpe =:= typeOf[String]  => Primitive(name, tpe)
        case tpe if tpe =:= typeOf[Boolean] => Primitive(name, tpe)
        case tpe if tpe =:= typeOf[Short]   => Primitive(name, tpe)
        case tpe if tpe =:= typeOf[Int]     => Primitive(name, tpe)
        case tpe if tpe =:= typeOf[Long]    => Primitive(name, tpe)
        case tpe if tpe =:= typeOf[Float]   => Primitive(name, tpe)
        case tpe if tpe =:= typeOf[Double]  => Primitive(name, tpe)
        case tpe if tpe.erasure =:= typeOf[Option[Any]] =>
          val innerType = tpe.asInstanceOf[TypeRefApi].args.head
          OptionBuilder(matchField(innerType, name))
        case tpe if tpe.typeSymbol.isClass && tpe.typeSymbol.asClass.isCaseClass =>
          CaseClassBuilder(name, expandMethod(tpe).map { case (t, s) => matchField(t, s) })
        case tpe if allowUnknownTypes => Primitive(name, tpe)
        case tpe =>
          c.abort(c.enclosingPosition, s"${T.tpe} is unsupported at $tpe")
      }

    def expandMethod(outerTpe: Type): Vector[(Type, String)] =
      outerTpe.declarations
        .collect { case m: MethodSymbol if m.isCaseAccessor => m }
        .map { accessorMethod =>
          val fieldName = accessorMethod.name.toString
          val fieldType = accessorMethod.returnType.asSeenFrom(outerTpe, outerTpe.typeSymbol.asClass)
          (fieldType, fieldName)
        }
        .toVector

    val builder = matchField(T.tpe, "")
    if (builder.columnTypes.isEmpty)
      c.abort(c.enclosingPosition, s"Case class ${T.tpe} has no primitive types we were able to extract")
    val scheme = if (isNumbered(T.tpe)) Indexed else namingScheme
    val tree = FieldBuilder.toFieldsTree(builder, scheme)
    c.Expr[cascading.tuple.Fields](tree)
  }