Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
package io.github.arainko.ducktape.internal

import scala.quoted.*
import io.github.arainko.ducktape.internal.Context.NamedTuples

private[ducktape] sealed trait Context {
type F <: Fallible

def summoner: Summoner[F]
def transformationSite: TransformationSite
def namedTuples: Option[NamedTuples]

final def reifyPlan[FF <: Fallible](value: Plan[Erroneous, F])(using ev: Plan[Erroneous, F] =:= Plan[Erroneous, FF]) =
ev(value)

final def toTotal: Context.Total = Context.Total(transformationSite)
final def toTotal: Context.Total = Context.Total(transformationSite, namedTuples)
}

private[ducktape] object Context {
type Of[F0 <: Fallible] = Context { type F = F0 }

// unappliedNamedTuple is the type lambda [Names, Values] =>> NamedTuple[Names, Values], used to harvest its type symbol later on
final class NamedTuples private (private val unappliedNamedTuple: Type[?]) {
def isNamedTuple(tpe: Type[?])(using Quotes) =
tpe.repr.dealias.typeSymbol == unappliedNamedTuple.repr.typeSymbol
}

object NamedTuples {
def create(using Quotes): Option[NamedTuples] = {
import quotes.reflect.*
Symbol
.requiredModule("scala.NamedTuple")
.declaredType("NamedTuple")
.headOption
.map(sym => NamedTuples(sym.typeRef.asType))
}
}

transparent inline def current(using ctx: Context): ctx.type = ctx

extension [F <: Fallible](self: Context.Of[F]) {
Expand All @@ -25,13 +46,15 @@ private[ducktape] object Context {
wrapperType: WrapperType[G],
transformationSite: TransformationSite,
summoner: Summoner.PossiblyFallible[G],
mode: TransformationMode[G]
mode: TransformationMode[G],
namedTuples: Option[NamedTuples]
) extends Context {
final type F = Fallible
}

case class Total(
transformationSite: TransformationSite
transformationSite: TransformationSite,
namedTuples: Option[NamedTuples]
) extends Context {
final type F = Nothing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ private[ducktape] object FallibleTransformations {
WrapperType.create[F],
TransformationSite.fromStringExpr(transformationSite),
Summoner.PossiblyFallible[F],
TransformationMode.create(F)
TransformationMode.create(F),
Context.NamedTuples.create
)

val sourceStruct = Structure.of[A](Path.empty(Type.of[A]))
Expand Down Expand Up @@ -53,7 +54,8 @@ private[ducktape] object FallibleTransformations {
WrapperType.create[F],
TransformationSite.fromStringExpr(transformationSite),
Summoner.PossiblyFallible[F],
TransformationMode.create(F)
TransformationMode.create(F),
Context.NamedTuples.create
)

val sourceStruct = Structure.of[A](Path.empty(Type.of[A]))
Expand Down Expand Up @@ -95,7 +97,8 @@ private[ducktape] object FallibleTransformations {
WrapperType.create[F],
TransformationSite.Transformation,
Summoner.PossiblyFallible[F],
TransformationMode.create(F)
TransformationMode.create(F),
Context.NamedTuples.create
)

val sourceStruct = Structure.of[A](Path.empty(Type.of[A]))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ private[ducktape] object Planner {
)(using Quotes, Depth, Context.Of[F], PlanFlags, Flag.Linter): Option[Plan[Erroneous, F]] =
PartialFunction.condOpt(Context.current *: structs) {
case (
ctx @ Context.PossiblyFallible(_, _, _, mode: TransformationMode.FailFast[f]),
ctx @ Context.PossiblyFallible(_, _, _, mode: TransformationMode.FailFast[f], _),
source @ Wrapped(tpe, _, path, underlying),
dest
) =>
Expand All @@ -464,7 +464,7 @@ private[ducktape] object Planner {
}

case (
ctx @ Context.PossiblyFallible(_, _, _, TransformationMode.Accumulating(mode, Some(localMode))),
ctx @ Context.PossiblyFallible(_, _, _, TransformationMode.Accumulating(mode, Some(localMode)), _),
source @ Wrapped(tpe, _, path, underlying),
dest
) =>
Expand All @@ -482,7 +482,7 @@ private[ducktape] object Planner {
}

case (
ctx @ Context.PossiblyFallible(_, _, _, TransformationMode.Accumulating(mode, None)),
ctx @ Context.PossiblyFallible(_, _, _, TransformationMode.Accumulating(mode, None), _),
source @ Wrapped(tpe, _, path, underlying),
dest
) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private[ducktape] object Structure {
.to(VectorMap)

val kind =
if Type.of[A].repr.dealias.typeSymbol.fullName == "scala.NamedTuple$.NamedTuple" then {
if Type.of[A].isNamedTuple then {
val normalizedErasedTupleTpe = Tuples.rollup(typeElems.toVector)
Structure.Product.Kind.NamedTuple(normalizedErasedTupleTpe)
} else Structure.Product.Kind.CaseClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ private[ducktape] object TotalTransformations {
configs: Expr[Seq[Field[A, B] | Case[A, B]]]
)(using Quotes): Expr[B] = {
given Context.Total(
TransformationSite.fromStringExpr(transformationSite)
TransformationSite.fromStringExpr(transformationSite),
Context.NamedTuples.create
)

val (config, flags) = Configuration.parse(configs, ConfigParser.total)
Expand All @@ -43,7 +44,8 @@ private[ducktape] object TotalTransformations {
)(using Quotes) = {

given Context.Total(
TransformationSite.Transformation
TransformationSite.Transformation,
Context.NamedTuples.create
)

val sourceStruct = Structure.of[A](Path.empty(Type.of[A]))
Expand Down Expand Up @@ -72,7 +74,8 @@ private[ducktape] object TotalTransformations {
configs: Expr[Seq[Field[A, Args] | Case[A, Args]]]
)(using Quotes) = {
given Context.Total(
TransformationSite.fromStringExpr(transformationSite)
TransformationSite.fromStringExpr(transformationSite),
Context.NamedTuples.create
)

val sourceStruct = Structure.of[A](Path.empty(Type.of[A]))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import scala.annotation.tailrec
import scala.quoted.*

private[ducktape] object Tuples {

object Named {
def AnyNamedTuple(using Quotes): Option[Type[?]] = {
import quotes.reflect.*
Symbol.requiredModule("scala.NamedTuple").declaredType("AnyNamedTuple").headOption.map(_.typeRef.asType)
}
}

def unroll(tpe: Type[?])(using Quotes): List[quotes.reflect.TypeRepr] = {
@tailrec def loop(using Quotes)(curr: Type[?], acc: List[quotes.reflect.TypeRepr]): List[quotes.reflect.TypeRepr] = {
import quotes.reflect.*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ private[ducktape] object WrapperType {
def unapply(using Quotes, Context)(tpe: Type[?]) =
Context.current match {
case ctx: Context.PossiblyFallible[?] => ctx.wrapperType.unapply(tpe)
case Context.Total(_) => None
case Context.Total(_, _) => None
}

case object Optional extends WrapperType[Option] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import io.github.arainko.ducktape.internal.Structure.Product.Kind

import scala.quoted.*

case class SomeCaseClass()

extension (tpe: Type[? <: AnyKind]) {
private[ducktape] def fullName(using Quotes): String = {
import quotes.reflect.*
Expand All @@ -13,6 +15,13 @@ extension (tpe: Type[? <: AnyKind]) {

private[ducktape] def repr(using Quotes): quotes.reflect.TypeRepr =
quotes.reflect.TypeRepr.of(using tpe)

private[ducktape] def isNamedTuple(using Context, Quotes): Boolean = {
Context.current.namedTuples.match {
case None => false
case Some(namedTuples) => namedTuples.isNamedTuple(tpe)
}
}
}

extension (expr: Expr[Any]) {
Expand Down
Loading