Skip to content

Commit

Permalink
WIP: Migration RowParserImpl
Browse files Browse the repository at this point in the history
  • Loading branch information
cchantep committed Aug 17, 2022
1 parent fe4c264 commit 25f953a
Show file tree
Hide file tree
Showing 9 changed files with 548 additions and 299 deletions.
2 changes: 1 addition & 1 deletion core/src/main/scala-2/anorm/Macro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ object Macro extends MacroOptions {
def psz = params.size

if (names.size < psz) {
c.abort(c.enclosingPosition, s"no column name for parameters: ${show(names)} < $params")
c.abort(c.enclosingPosition, s"no column name for parameters: ${names.map(n => show(n)) mkString ", "} < ${params.map(_.name) mkString ", "}")

} else {
parserImpl[T](c) { (t, _, i) =>
Expand Down
6 changes: 1 addition & 5 deletions core/src/main/scala-2/anorm/macros/SealedRowParserImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,8 @@ private[anorm] object SealedRowParserImpl {
val caseName = TermName(c.freshName("discriminated"))
val key = q"$discriminate(${subclass.typeSymbol.fullName})"
val caseDecl = q"val $caseName = $key"
val subtype = { // TODO: typeParams is not supported anyway
if (subclass.typeSymbol.asClass.typeParams.isEmpty) subclass
else subclass.erasure
}

(key, caseDecl, cq"`$caseName` => implicitly[anorm.RowParser[$subtype]]")
(key, caseDecl, cq"`$caseName` => implicitly[anorm.RowParser[$subclass]]")
}

lazy val supported = q"List(..${cases.map(_._1)})"
Expand Down
66 changes: 46 additions & 20 deletions core/src/main/scala-3/anorm/Macro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -265,21 +265,28 @@ object Macro extends MacroOptions with macros.ValueColumn with macros.ValueToSta

// ---

private def namedParserImpl[T](using Quotes, Type[T]): Expr[RowParser[T]] =
withColumn[T] { col =>
parserImpl[T] { (n, _) =>
'{ anorm.SqlParser.get[T](${ Expr(n) })($col) }
private def namedParserImpl[T](using q: Quotes, tpe: Type[T]): Expr[RowParser[T]] =
parserImpl[T](q) { (tpr, n, _) =>
tpr.asType match {
case '[t] =>
withColumn[t] { col =>
'{ SqlParser.get[t](${ Expr(n) })($col) }
}
}
}

private def namedParserImpl1[T](
naming: Expr[ColumnNaming]
)(using Quotes, Type[T], Type[ColumnNaming]): Expr[RowParser[T]] =
withColumn[T] { col =>
parserImpl[T] { (n, _) =>
'{ anorm.SqlParser.get[T]($naming(${ Expr(n) }))($col) }
)(using q: Quotes, tpe: Type[T], colNme: Type[ColumnNaming]): Expr[RowParser[T]] = {
parserImpl[T](q) { (tpr, n, _) =>
tpr.asType match {
case '[t] =>
withColumn[t] { col =>
'{ SqlParser.get[t]($naming(${ Expr(n) }))($col) }
}
}
}
}

private def namedParserImpl2[T](
naming: Expr[ColumnNaming],
Expand Down Expand Up @@ -316,13 +323,16 @@ object Macro extends MacroOptions with macros.ValueColumn with macros.ValueToSta
report.errorAndAbort(s"no column name for parameters: ${ns.mkString(", ")} < $params")

} else {
parserImpl[T] { (_, i) =>
parserImpl[T](q) { (tpr, _, i) =>
ns.lift(i) match {
case Some(n) =>
withColumn[T] { col =>
val cn = naming(Expr(n))
tpr.asType match {
case '[t] =>
withColumn[t] { col =>
val cn = naming(Expr(n))

'{ SqlParser.get[T]($cn)($col) }
'{ SqlParser.get[t]($cn)($col) }
}
}

case _ =>
Expand All @@ -332,10 +342,13 @@ object Macro extends MacroOptions with macros.ValueColumn with macros.ValueToSta
}
}

private def offsetParserImpl[T](offset: Expr[Int])(using Quotes, Type[T]): Expr[RowParser[T]] =
withColumn[T] { col =>
parserImpl[T] { (_, i) =>
'{ anorm.SqlParser.get[T]($offset + ${ Expr(i + 1) })($col) }
private def offsetParserImpl[T](offset: Expr[Int])(using q: Quotes, tpe: Type[T]): Expr[RowParser[T]] =
parserImpl[T](q) { (tpr, _, i) =>
tpr.asType match {
case '[t] =>
withColumn[t] { col =>
'{ SqlParser.get[t]($offset + ${ Expr(i + 1) })($col) }
}
}
}

Expand All @@ -361,9 +374,22 @@ object Macro extends MacroOptions with macros.ValueColumn with macros.ValueToSta
): Expr[RowParser[T]] =
macros.SealedRowParserImpl[T](naming, discriminate)

private def parserImpl[T](genGet: (String, Int) => Expr[RowParser[T]])(using Quotes, Type[T]): Expr[RowParser[T]] = {
// TODO: anorm.macros.RowParserImpl[T](c)(genGet)
'{ ??? }
inline private def withParser[T](f: RowParser[T] => (Row => SqlResult[T])): RowParser[T] = new RowParser[T] { self =>
lazy val underlying = f(self)

def apply(row: Row): SqlResult[T] = underlying(row)
}

private def parserImpl[T](
q: Quotes
)(genGet: (q.reflect.TypeRepr, String, Int) => Expr[RowParser[_]])(using Type[T]): Expr[RowParser[T]] = {
given quotes: Quotes = q

'{
withParser { self =>
${ macros.RowParserImpl[T](q, 'self)(genGet) }
}
}
}

// ---
Expand Down Expand Up @@ -516,7 +542,7 @@ object Macro extends MacroOptions with macros.ValueColumn with macros.ValueToSta
}
}

inline private[anorm] def withSelfToParameterList[T](
inline private def withSelfToParameterList[T](
f: ToParameterList[T] => (T => List[NamedParameter])
): ToParameterList[T] = new ToParameterList[T] { self =>
lazy val underlying = f(self)
Expand Down
200 changes: 200 additions & 0 deletions core/src/main/scala-3/anorm/macros/RowParserImpl.scala
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))
*/

'{ ??? }
}
}
Loading

0 comments on commit 25f953a

Please sign in to comment.