-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
548 additions
and
299 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
package anorm.macros | ||
|
||
import scala.quoted.{ Expr, Quotes, Type } | ||
|
||
import anorm.{ Column, Row, RowParser, SqlResult } | ||
import anorm.Macro.debugEnabled | ||
|
||
private[anorm] object RowParserImpl { | ||
def apply[A]( | ||
q: Quotes, | ||
forwardExpr: Expr[RowParser[A]] | ||
)( | ||
genGet: (q.reflect.TypeRepr, String, Int) => Expr[RowParser[_]] | ||
)(using tpe: Type[A], parserTpe: Type[RowParser]): Expr[Row => SqlResult[A]] = { | ||
given quotes: Quotes = q | ||
|
||
import q.reflect.* | ||
|
||
val (repr, aTArgs) = TypeRepr.of[A](using tpe) match { | ||
case tpr @ AppliedType(_, args) => | ||
tpr -> args.collect { | ||
case repr: TypeRepr => | ||
repr | ||
} | ||
|
||
case tpr => | ||
tpr -> List.empty[TypeRepr] | ||
} | ||
|
||
@inline def abort(msg: String) = report.errorAndAbort(msg) | ||
|
||
val tpeSym = repr.typeSymbol | ||
|
||
if (!tpeSym.isClassDef || !tpeSym.flags.is(Flags.Case)) { | ||
abort(s"case class expected: ${repr.show}") | ||
} | ||
|
||
// --- | ||
|
||
val ctor = tpeSym.primaryConstructor | ||
|
||
val (boundTypes, properties) = ctor.paramSymss match { | ||
case targs :: params :: Nil if targs.forall(_.isType) => { | ||
val boundTps = targs.zip(aTArgs).toMap | ||
|
||
boundTps -> params | ||
} | ||
|
||
case params :: Nil => | ||
Map.empty[Symbol, TypeRepr] -> params | ||
|
||
case params :: _ => { | ||
report.info( | ||
s"${repr.show} constructor has multiple list of parameters. As for unapply, only for the first one will be considered" | ||
) | ||
|
||
Map.empty[Symbol, TypeRepr] -> params | ||
} | ||
|
||
case _ => | ||
report.errorAndAbort(s"${repr.show} constructor has no parameter") | ||
} | ||
|
||
if (properties.isEmpty) { | ||
abort(s"parsed data cannot be passed as parameter: $ctor") | ||
} | ||
|
||
/* TODO | ||
val colTpe = c.weakTypeTag[Column[_]].tpe | ||
val parserTpe = c.weakTypeTag[RowParser[_]].tpe | ||
val boundTypes: Map[String, Type] = Inspect.boundTypes(c)(tpe) | ||
val forwardName = TermName(c.freshName("forward")) | ||
*/ | ||
|
||
val debug = { | ||
if (debugEnabled) report.info(_: String) | ||
else (_: String) => {} | ||
} | ||
|
||
val resolv = ImplicitResolver[A](q).resolver(forwardExpr, Map.empty, debug)(parserTpe) | ||
|
||
/* TODO | ||
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: ${parser.show}") | ||
} | ||
c.Expr[RowParser[T]](c.typecheck(parser)) | ||
*/ | ||
|
||
'{ ??? } | ||
} | ||
} |
Oops, something went wrong.