All Downloads are FREE. Search and download functionalities are using the official Maven repository.

anorm.macros.RowParserImpl.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 
 */

package anorm.macros

import scala.reflect.macros.whitebox

import anorm.{ Column, RowParser }
import anorm.Macro.debugEnabled
import anorm.macros.Inspect.pretty

private[anorm] object RowParserImpl {
  def apply[T: c.WeakTypeTag](
      c: whitebox.Context
  )(genGet: (c.universe.Type, String, Int) => c.universe.Tree): c.Expr[RowParser[T]] = {
    val tpe                        = c.weakTypeTag[T].tpe
    @inline def abort(msg: String) = c.abort(c.enclosingPosition, msg)

    if (!tpe.typeSymbol.isClass || !tpe.typeSymbol.asClass.isCaseClass) {
      abort(s"case class expected: $tpe")
    }

    val ctor = tpe.decl(c.universe.termNames.CONSTRUCTOR).asMethod

    if (ctor.paramLists.isEmpty) {
      abort(s"parsed data cannot be passed as parameter: $ctor")
    }

    val colTpe    = c.weakTypeTag[Column[_]].tpe
    val parserTpe = c.weakTypeTag[RowParser[_]].tpe

    import c.universe._

    val boundTypes: Map[String, Type] = Inspect.boundTypes(c)(tpe)
    val forwardName                   = TermName(c.freshName("forward"))

    val resolveImplicit: (Name, Type, Type) => Implicit[Type, Name, Tree] =
      ImplicitResolver(c)(tpe, boundTypes, forwardName)

    // ---

    val (x, m, body, _, hasSelfRef) =
      ctor.paramLists.foldLeft[(Tree, Tree, Tree, Int, Boolean)]((EmptyTree, EmptyTree, EmptyTree, 0, false)) {
        case ((xa, ma, bs, ia, sr), pss) =>
          val (xb, mb, vs, ib, selfRef) =
            pss.foldLeft((xa, ma, List.empty[Tree], ia, sr)) {
              case ((xtr, mp, ps, pi, sref), term: TermSymbol) => {
                val tn = term.name.toString
                val tt = {
                  val t = term.typeSignature

                  boundTypes.getOrElse(t.typeSymbol.fullName, t)
                  // TODO: term.isParamWithDefault
                }

                // Try to resolve `Column[tt]`
                resolveImplicit(term.name, tt, colTpe) match {
                  case Implicit.Unresolved() => // No `Column[tt]` ...
                    // ... try to resolve `RowParser[tt]`
                    resolveImplicit(term.name, tt, parserTpe) match {
                      case Implicit.Unresolved() =>
                        abort(s"cannot find $colTpe nor $parserTpe for ${term.name}:$tt in $ctor")

                      case Implicit(_, _, pr, _, s) => {
                        // Use an existing `RowParser[T]` as part
                        pq"${term.name}" match {
                          case b @ Bind(bn, _) => {
                            val bt = q"${bn.toTermName}"

                            xtr match {
                              case EmptyTree =>
                                (pr, b, List[Tree](bt), pi + 1, s || sref)

                              case _ => (q"$xtr ~ $pr", pq"anorm.~($mp, $b)", bt :: ps, pi + 1, s || sref)

                            }
                          }

                          case _ =>
                            abort(s"unsupported $colTpe nor $parserTpe for ${term.name}:$tt in $ctor")
                        }
                      }
                    }

                  case Implicit(_, _, itree, _, _) => {
                    // Generate a `get` for the `Column[T]`
                    val get = genGet(tt, tn, pi)

                    pq"${term.name}" match {
                      case b @ Bind(bn, _) => {
                        val bt = q"${bn.toTermName}"

                        xtr match {
                          case EmptyTree =>
                            (get, b, List[Tree](bt), pi + 1, sref)

                          case _ => (q"$xtr ~ $get($itree)", pq"anorm.~($mp, $b)", bt :: ps, pi + 1, sref)

                        }
                      }

                      case _ =>
                        abort(s"unsupported $colTpe nor $parserTpe for ${term.name}:$tt: ${show(itree)}")
                    }
                  }
                }
              }

              case (state, sym) => {
                c.warning(c.enclosingPosition, s"unexpected symbol: $sym")
                state
              }
            }

          val by = bs match {
            case EmptyTree => q"new $tpe(..${vs.reverse})"
            case xs        => q"$xs(..${vs.reverse})"
          }

          (xb, mb, by, ib, selfRef)
      }

    val caseDef = cq"$m => { $body }"
    val patMat  = q"$x.map[$tpe] { _ match { case $caseDef } }"
    val parser =
      if (!hasSelfRef) patMat
      else {
        val generated = TypeName(c.freshName("Generated"))
        val rowParser = TermName(c.freshName("rowParser"))

        q"""{
        final class $generated() {
          val ${forwardName} = 
            anorm.RowParser[$tpe]($rowParser)

          def $rowParser: anorm.RowParser[$tpe] = $patMat
        }

        new $generated().$rowParser
      }"""
      }

    if (debugEnabled) {
      c.echo(c.enclosingPosition, s"row parser generated for $tpe: ${pretty(c)(parser)}")
    }

    c.Expr[RowParser[T]](c.typecheck(parser))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy