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

io.getquill.sources.SourceMacro.scala Maven / Gradle / Ivy

There is a newer version: 4.6.0
Show newest version
package io.getquill.sources

import scala.reflect.macros.whitebox.Context
import io.getquill._
import io.getquill.ast.{ Action => _, Query => _, _ }
import io.getquill.quotation.Quotation
import io.getquill.quotation.Quoted
import io.getquill.quotation.FreeVariables
import io.getquill.util.Messages._
import io.getquill.quotation.Bindings

trait SourceMacro extends Quotation with ActionMacro with QueryMacro with ResolveSourceMacro {
  val c: Context
  import c.universe.{ Function => _, Ident => _, _ }

  protected def prepare(ast: Ast, params: List[Ident]): Tree

  def run[R, S](quoted: Expr[Quoted[Any]])(implicit r: WeakTypeTag[R], s: WeakTypeTag[S]): Tree = runExpr(quoted, true)(r, s)

  def runSingle[R, S](quoted: Expr[Quoted[Any]])(implicit r: WeakTypeTag[R], s: WeakTypeTag[S]): Tree = runExpr(quoted, false)(r, s)

  private def runExpr[R, S](quoted: Expr[Quoted[Any]], returnList: Boolean)(implicit r: WeakTypeTag[R], s: WeakTypeTag[S]): Tree = {
    implicit val t = c.WeakTypeTag(quoted.actualType.baseType(c.weakTypeOf[Quoted[Any]].typeSymbol).typeArgs.head)

    val ast = this.ast(quoted)

    FreeVariables(ast) match {
      case free if free.isEmpty =>
      case free =>
        c.fail(s"""
          |Found the following free variables: ${free.mkString(", ")}.
          |Quotations can't reference values outside their scope directly. 
          |In order to bind runtime values to a quotation, please use the method `lift`.
          |Example: `def byName(n: String) = quote(query[Person].filter(_.name == lift(n)))`
        """.stripMargin)
    }

    val inPlaceParams = bindingsTree(q"quoted", quoted.tree.tpe)

    t.tpe.typeSymbol.fullName.startsWith("scala.Function") match {

      case true =>
        val bodyType = c.WeakTypeTag(t.tpe.typeArgs.takeRight(1).head)
        val params = (1 until t.tpe.typeArgs.size).map(i => Ident(s"p$i")).toList
        run(quoted.tree, FunctionApply(ast, params), inPlaceParams, params.zip(paramsTypes(t)), returnList)(r, s, bodyType)

      case false =>
        run(quoted.tree, ast, inPlaceParams, Nil, returnList)(r, s, t)
    }
  }

  private def bindingsTree(tree: Tree, tpe: Type) = {
    Bindings(c)(tree, tpe)
      .map {
        case (symbol, tree) =>
          (Ident(symbol.name.decodedName.toString), (symbol.typeSignature.resultType, tree))
      }.toMap
  }

  private def run[R, S, T](quotedTree: Tree, ast: Ast, inPlaceParams: collection.Map[Ident, (Type, Tree)], params: List[(Ident, Type)], returnList: Boolean)(implicit r: WeakTypeTag[R], s: WeakTypeTag[S], t: WeakTypeTag[T]): Tree =
    ast match {
      case ast if ((t.tpe.erasure <:< c.weakTypeTag[Action[Any]].tpe.erasure)) =>
        runAction[S, T](quotedTree, ast, inPlaceParams, params)

      case ast =>
        runQuery(quotedTree, ast, inPlaceParams, params, returnList)(r, s, queryType(t.tpe))
    }

  private def queryType(tpe: Type) =
    if (tpe <:< c.typeOf[Query[_]])
      c.WeakTypeTag(tpe.baseType(c.typeOf[Query[_]].typeSymbol).typeArgs.head)
    else
      c.WeakTypeTag(tpe)

  private def ast[T](quoted: Expr[Quoted[T]]) =
    unquote[Ast](quoted.tree).getOrElse {
      Dynamic(quoted.tree)
    }

  private def paramsTypes[T](implicit t: WeakTypeTag[T]) =
    t.tpe.typeArgs.dropRight(1)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy