From 3479efc419e600050b1c7459fecc83b93c140ce0 Mon Sep 17 00:00:00 2001 From: Aleksander Rainko Date: Sun, 15 Oct 2023 02:39:49 +0200 Subject: [PATCH] sprinkle in logs --- .gitignore | 1 + .../io/github/arainko/ducktape/Path.scala | 1 + .../arainko/ducktape/PathSelector.scala | 22 +++- .../io/github/arainko/ducktape/Plan.scala | 2 +- .../arainko/ducktape/PlanInterpreter.scala | 11 +- .../io/github/arainko/ducktape/Planner.scala | 109 +++++++++--------- .../github/arainko/ducktape/Playground.scala | 6 +- .../arainko/ducktape/internal/Logger.scala | 61 ++++++---- .../io/github/arainko/tooling}/Debug.scala | 15 +-- .../{FullName.scala => Metainformation.scala} | 12 +- 10 files changed, 134 insertions(+), 106 deletions(-) rename {ducktapeNext/src/main/scala/io/github/arainko/ducktape/internal => tooling/src/main/scala/io/github/arainko/tooling}/Debug.scala (88%) rename tooling/src/main/scala/io/github/arainko/tooling/{FullName.scala => Metainformation.scala} (62%) diff --git a/.gitignore b/.gitignore index 8fb7ff22..cf946314 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ metals.sbt target/ ast.* +sbt-launch.jar diff --git a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Path.scala b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Path.scala index 3d450565..8fc750dd 100644 --- a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Path.scala +++ b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Path.scala @@ -3,6 +3,7 @@ package io.github.arainko.ducktape import scala.quoted.* import io.github.arainko.ducktape.internal.modules.* import io.github.arainko.ducktape.internal.* +import io.github.arainko.ducktape.internal.Debug opaque type Path = Vector[Path.Segment] diff --git a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/PathSelector.scala b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/PathSelector.scala index d14b481a..abecafb8 100644 --- a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/PathSelector.scala +++ b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/PathSelector.scala @@ -2,6 +2,7 @@ package io.github.arainko.ducktape import scala.quoted.* import io.github.arainko.ducktape.internal.macros.DebugMacros +import io.github.arainko.ducktape.internal.Logger import scala.collection.mutable.ListBuffer import scala.annotation.tailrec @@ -15,24 +16,37 @@ object PathSelector { term match { case Inlined(_, _, tree) => + Logger.debug("Matched 'Inlined', recursing...") recurse(acc, tree) case Lambda(_, tree) => + Logger.debug("Matched 'Lambda', recursing...") recurse(acc, tree) case Block(_, tree) => + Logger.debug("Matched 'Block', recursing...") recurse(acc, tree) case select @ Select(tree, name) => + Logger.debug(s"Matched 'Select' (matching field access) with name = $name") recurse(acc.prepended(Path.Segment.Field(select.tpe.asType, name)), tree) case TypeApply(Apply(TypeApply(Select(Ident(_), "at"), _), tree :: Nil), tpe :: Nil) => + Logger.debug(s"Matched 'TypeApply' (matching '.at')", tpe.tpe.asType) recurse(acc.prepended(Path.Segment.Case(tpe.tpe.asType)), tree) // Function arg selection can only happen as the first selection (aka the last one to be parsed) so this not being recursive is fine (?) - case TypeApply(Select(Apply(Select(Ident(_), "selectDynamic"), Literal(StringConstant(argName)) :: Nil), "$asInstanceOf$"), argTpe :: Nil) => + case TypeApply( + Select(Apply(Select(Ident(_), "selectDynamic"), Literal(StringConstant(argName)) :: Nil), "$asInstanceOf$"), + argTpe :: Nil + ) => + Logger.debug(s"Matched 'selectDynamic' (matching a function arg selector) with name = $argName") acc.prepended(Path.Segment.Field(argTpe.tpe.asType, argName)) - case Ident(_) => acc - case other => report.errorAndAbort(other.show(using Printer.TreeShortCode)) + case Ident(_) => + Logger.debug(s"Matched 'Ident', returning...") + acc + case other => + Logger.debug(s"Matched an unexpected term") + report.errorAndAbort(other.show(using Printer.TreeShortCode)) } } - Some(recurse(Path.empty, expr)) + Some(Logger.loggedInfo("Parsed path")(recurse(Path.empty, expr))) } } diff --git a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Plan.scala b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Plan.scala index ede02727..b96cc325 100644 --- a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Plan.scala +++ b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Plan.scala @@ -145,7 +145,7 @@ object Plan { def unapply[E <: Plan.Error](plan: Plan[E]): (Type[?], Type[?]) = (plan.sourceTpe, plan.destTpe) - given debug: Debug[Plan[?]] = Debug.derived + given debug[E <: Plan.Error]: Debug[Plan[E]] = Debug.derived final case class Context(root: Type[?], path: Path) derives Debug { def add(segment: Path.Segment): Context = copy(path = path.appended(segment)) diff --git a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/PlanInterpreter.scala b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/PlanInterpreter.scala index 0eed9f71..5cbe8653 100644 --- a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/PlanInterpreter.scala +++ b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/PlanInterpreter.scala @@ -58,15 +58,14 @@ object PlanInterpreter { )(using Quotes): Expr[B] = { import quotes.reflect.* + // Logger.info("Creating transformation") + val plan = Planner.betweenTypes[A, B] val config = Configuration.parse(configs) val reconfiguredPlan = config.foldLeft(plan) { (plan, config) => plan.configure(config) } - // println(s"OG PLAN: ${plan.show}") - // println() - // println(s"CONFIG: ${Debug.show(config)}") - // println() - // println(s"CONF PLAN: ${reconfiguredPlan.show}") - // println() + Logger.debug("Original plan", plan) + Logger.debug("Config", config) + Logger.debug("Reconfigured plan", reconfiguredPlan) reconfiguredPlan.refine match { case Left(errors) => val rendered = errors.map(err => s"${err.message} @ ${err.sourceContext.render}").mkString("\n") diff --git a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Planner.scala b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Planner.scala index 66ff05fe..6a36e386 100644 --- a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Planner.scala +++ b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Planner.scala @@ -2,7 +2,7 @@ package io.github.arainko.ducktape import io.github.arainko.ducktape.Plan.Context import io.github.arainko.ducktape.internal.modules.* -import io.github.arainko.ducktape.internal.Debug +import io.github.arainko.ducktape.internal.{Debug, Logger} import scala.quoted.* @@ -35,71 +35,72 @@ object Planner { )(using Quotes): Plan[Plan.Error] = { import quotes.reflect.* - (source.force -> dest.force) match { - case (source: Product, dest: Function) => - planProductFunctionTransformation(source, dest, sourceContext, destContext) + Logger.loggedInfo("Plan"): + (source.force -> dest.force) match { + case (source: Product, dest: Function) => + planProductFunctionTransformation(source, dest, sourceContext, destContext) - case UserDefinedTransformation(transformer) => - Plan.UserDefined(source.tpe, dest.tpe, sourceContext, destContext, transformer) + case UserDefinedTransformation(transformer) => + Plan.UserDefined(source.tpe, dest.tpe, sourceContext, destContext, transformer) - case (source, dest) if source.tpe.repr <:< dest.tpe.repr => - Plan.Upcast(source.tpe, dest.tpe, sourceContext, destContext) + case (source, dest) if source.tpe.repr <:< dest.tpe.repr => + Plan.Upcast(source.tpe, dest.tpe, sourceContext, destContext) - case Structure('[Option[srcTpe]], srcName) -> Structure('[Option[destTpe]], destName) => - Plan.BetweenOptions( - Type[srcTpe], - Type[destTpe], - sourceContext, - destContext, - recurseAndCreatePlan[srcTpe, destTpe](sourceContext, destContext) - ) + case Structure('[Option[srcTpe]], srcName) -> Structure('[Option[destTpe]], destName) => + Plan.BetweenOptions( + Type[srcTpe], + Type[destTpe], + sourceContext, + destContext, + recurseAndCreatePlan[srcTpe, destTpe](sourceContext, destContext) + ) - case Structure('[a], _) -> Structure('[Option[destTpe]], destName) => - Plan.BetweenNonOptionOption( - Type[a], - Type[destTpe], - sourceContext, - destContext, - recurseAndCreatePlan[a, destTpe](sourceContext, destContext) - ) + case Structure('[a], _) -> Structure('[Option[destTpe]], destName) => + Plan.BetweenNonOptionOption( + Type[a], + Type[destTpe], + sourceContext, + destContext, + recurseAndCreatePlan[a, destTpe](sourceContext, destContext) + ) - case Structure(source @ '[Iterable[srcTpe]], srcName) -> Structure(dest @ '[Iterable[destTpe]], destName) => - Plan.BetweenCollections( - dest, - Type[srcTpe], - Type[destTpe], - sourceContext, - destContext, - recurseAndCreatePlan[srcTpe, destTpe](sourceContext, destContext) - ) + case Structure(source @ '[Iterable[srcTpe]], srcName) -> Structure(dest @ '[Iterable[destTpe]], destName) => + Plan.BetweenCollections( + dest, + Type[srcTpe], + Type[destTpe], + sourceContext, + destContext, + recurseAndCreatePlan[srcTpe, destTpe](sourceContext, destContext) + ) - case (source: Product, dest: Product) => - planProductTransformation(source, dest, sourceContext, destContext) + case (source: Product, dest: Product) => + planProductTransformation(source, dest, sourceContext, destContext) - case (source: Coproduct, dest: Coproduct) => - planCoproductTransformation(source, dest, sourceContext, destContext) + case (source: Coproduct, dest: Coproduct) => + planCoproductTransformation(source, dest, sourceContext, destContext) - case (source: Structure.Singleton, dest: Structure.Singleton) if source.name == dest.name => - Plan.BetweenSingletons(source.tpe, dest.tpe, sourceContext, destContext, dest.value) + case (source: Structure.Singleton, dest: Structure.Singleton) if source.name == dest.name => + Plan.BetweenSingletons(source.tpe, dest.tpe, sourceContext, destContext, dest.value) - case (source: ValueClass, dest) if source.paramTpe.repr <:< dest.tpe.repr => - Plan.BetweenWrappedUnwrapped(source.tpe, dest.tpe, sourceContext, destContext, source.paramFieldName) + case (source: ValueClass, dest) if source.paramTpe.repr <:< dest.tpe.repr => + Plan.BetweenWrappedUnwrapped(source.tpe, dest.tpe, sourceContext, destContext, source.paramFieldName) - case (source, dest: ValueClass) if source.tpe.repr <:< dest.paramTpe.repr => - Plan.BetweenUnwrappedWrapped(source.tpe, dest.tpe, sourceContext, destContext) + case (source, dest: ValueClass) if source.tpe.repr <:< dest.paramTpe.repr => + Plan.BetweenUnwrappedWrapped(source.tpe, dest.tpe, sourceContext, destContext) - case DerivedTransformation(transformer) => - Plan.Derived(source.tpe, dest.tpe, sourceContext, destContext, transformer) + case DerivedTransformation(transformer) => + Plan.Derived(source.tpe, dest.tpe, sourceContext, destContext, transformer) - case (source, dest) => - Plan.Error( - source.tpe, - dest.tpe, - sourceContext, - destContext, - s"Couldn't build a transformation plan between ${source.tpe.repr.show} and ${dest.tpe.repr.show}" - ) - } + case (source, dest) => + Plan.Error( + source.tpe, + dest.tpe, + sourceContext, + destContext, + s"Couldn't build a transformation plan between ${source.tpe.repr.show} and ${dest.tpe.repr.show}" + ) + } } private def planProductTransformation( diff --git a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Playground.scala b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Playground.scala index cc83de12..9d71bfa9 100644 --- a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Playground.scala +++ b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/Playground.scala @@ -1,11 +1,13 @@ package io.github.arainko.ducktape import io.github.arainko.ducktape.internal.macros.DebugMacros +import io.github.arainko.ducktape.internal.Debug import scala.annotation.nowarn import scala.deriving.Mirror import io.github.arainko.ducktape.Transformer.Debug import scala.reflect.TypeTest import scala.reflect.ClassTag +import scala.quoted.Quotes final case class Value(int: Int) extends AnyVal final case class ValueGen[A](int: A) extends AnyVal @@ -100,11 +102,11 @@ final case class ProdTest2(test: Test2) case class PersonFields2(int: Int, str: String, extra: Int) val fields: PersonFields2 = ??? - Debug.showCode { + // Debug.showCode { PlanInterpreter.transformBetween[PersonCostamCostam, PersonCostamCostam2]( ???, Field2.allMatching(a => a.p.p, fields) ) - } + // } def costam(int: Int, str: String): Int = ??? diff --git a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/internal/Logger.scala b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/internal/Logger.scala index aff4c574..6f9dffbf 100644 --- a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/internal/Logger.scala +++ b/ducktapeNext/src/main/scala/io/github/arainko/ducktape/internal/Logger.scala @@ -1,67 +1,80 @@ package io.github.arainko.ducktape.internal +import io.github.arainko.ducktape.Transformer +import io.github.arainko.ducktape.internal.{ Debug, Metainformation } + import scala.compiletime.* import scala.quoted.* -import io.github.arainko.ducktape.Transformer -import io.github.arainko.tooling.FullName +import scala.Ordering.Implicits.* private[ducktape] object Logger { - transparent inline given Level = Level.Info - given Output = Output.StdOut + // Logger Config + private transparent inline def level = Level.Off + private val output = Output.StdOut + private def filter(msg: String, meta: Metainformation) = true enum Level { - case Debug, Info, Off + case Off, Debug, Info + } + + object Level { + given Ordering[Level] = Ordering.by(_.ordinal) } enum Output { case StdOut, Report - final def print(msg: String)(using Quotes) = + final def print(msg: String, level: Level, meta: Metainformation)(using Quotes) = { + def colored(color: String & Singleton)(msg: String) = s"$color$msg${Console.RESET}" + def blue(msg: String) = colored(Console.BLUE)(msg) + def green(msg: String) = colored(Console.GREEN)(msg) + val formatted = s"${green(s"[${level.toString().toUpperCase()}]")} $msg ${blue(s"[$meta]")}" this match { - case StdOut => println(msg) - case Report => quotes.reflect.report.info(msg) + case StdOut => if (filter(msg, meta)) println(formatted) + case Report => if (filter(msg, meta)) quotes.reflect.report.info(formatted) } + } } - private val infoTag = s"${Console.GREEN}[INFO]${Console.RESET}" - private val debugTag = s"${Console.GREEN}[DEBUG]${Console.RESET}" - private def blue(msg: String) = s"${Console.BLUE}$msg${Console.RESET}" - - inline def loggedInfo[A](using Level, Output, FullName, Debug[A], Quotes)( + inline def loggedInfo[A](using Metainformation, Quotes)( inline msg: String - )(value: A) = { + )(value: A)(using Debug[A]) = { info(msg, value) value } - inline def info(inline msg: String)(using level: Level, output: Output, name: FullName, quotes: Quotes): Unit = + inline def info(inline msg: String)(using name: Metainformation, quotes: Quotes): Unit = inline level match { - case Level.Debug => () - case Level.Info => output.print(s"$infoTag $msg ${blue(s"[$name]")}") + case Level.Debug => if (level <= Level.Info) output.print(msg, Level.Info, name) + case Level.Info => if (level <= Level.Info) output.print(msg, Level.Info, name) case Level.Off => () } inline def info[A]( inline msg: String, value: A - )(using Level, Output, FullName, Debug[A], Quotes): Unit = + )(using Metainformation, Debug[A], Quotes): Unit = info(s"$msg: ${Debug.show(value)}") - inline def loggedDebug[A](using Level, Output, FullName, Debug[A], Quotes)( + inline def loggedDebug[A](using Level, Output, Metainformation, Quotes)( inline msg: String - )(value: A) = { + )(value: A)(using Debug[A]) = { debug(msg, value) value } - inline def debug(inline msg: String)(using level: Level, output: Output, name: FullName, quotes: Quotes): Unit = + inline def debug(inline msg: String)(using name: Metainformation, quotes: Quotes): Unit = inline level match { - case Level.Debug => output.print(s"$debugTag $msg ${blue(s"[$name]")}") - case Level.Info => output.print(s"$infoTag $msg ${blue(s"[$name]")}") + case Level.Debug => if (level <= Level.Debug) output.print(msg, Level.Debug, name) + case Level.Info => if (level <= Level.Debug) output.print(msg, Level.Debug, name) case Level.Off => () } - inline def debug[A](inline msg: String, value: A)(using level: Level, name: FullName, _debug: Debug[A], quotes: Quotes): Unit = + inline def debug[A](inline msg: String, value: A)(using + name: Metainformation, + _debug: Debug[A], + quotes: Quotes + ): Unit = debug(s"$msg: ${Debug.show(value)}") } diff --git a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/internal/Debug.scala b/tooling/src/main/scala/io/github/arainko/tooling/Debug.scala similarity index 88% rename from ducktapeNext/src/main/scala/io/github/arainko/ducktape/internal/Debug.scala rename to tooling/src/main/scala/io/github/arainko/tooling/Debug.scala index be69effd..7c0eec4f 100644 --- a/ducktapeNext/src/main/scala/io/github/arainko/ducktape/internal/Debug.scala +++ b/tooling/src/main/scala/io/github/arainko/tooling/Debug.scala @@ -2,9 +2,7 @@ package io.github.arainko.ducktape.internal import scala.quoted.* import scala.compiletime.* -import io.github.arainko.ducktape.internal.modules.* import scala.deriving.Mirror -import io.github.arainko.ducktape.Transformer2 import scala.collection.immutable.ListMap private[ducktape] trait Debug[A] { @@ -22,7 +20,7 @@ private[ducktape] object Debug { given tpe: Debug[Type[?]] with { extension (value: Type[?]) def show(using Quotes): String = { import quotes.reflect.* - s"Type.of[${Printer.TypeReprShortCode.show(value.repr)}]" + s"Type.of[${Printer.TypeReprShortCode.show(TypeRepr.of(using value))}]" } } @@ -44,9 +42,9 @@ private[ducktape] object Debug { .mkString("ListMap(", ", ", ")") } - given defered[A]: Debug[() => A] with { + given deferred[A]: Debug[() => A] with { extension (value: () => A) def show(using Quotes): String = - s"Lazy(...)" + s"Deferred(...)" } given expr[A]: Debug[Expr[A]] with { @@ -56,8 +54,7 @@ private[ducktape] object Debug { } } - - inline def derived[A](using A: Mirror.Of[A]) = + inline def derived[A](using A: Mirror.Of[A]): Debug[A] = inline A match { case given Mirror.ProductOf[A] => product case given Mirror.SumOf[A] => coproduct @@ -80,9 +77,9 @@ private[ducktape] object Debug { } } - private inline def coproduct[A](using A: Mirror.SumOf[A]): Debug[A] = new { + private inline def coproduct[A](using A: Mirror.SumOf[A]): Debug[A] = new { + private val instances = deriveForAll[A.MirroredElemTypes].toVector extension (self: A) def show(using Quotes): String = { - val instances = deriveForAll[A.MirroredElemTypes].toVector val ordinal = A.ordinal(self) instances(ordinal).show(self) } diff --git a/tooling/src/main/scala/io/github/arainko/tooling/FullName.scala b/tooling/src/main/scala/io/github/arainko/tooling/Metainformation.scala similarity index 62% rename from tooling/src/main/scala/io/github/arainko/tooling/FullName.scala rename to tooling/src/main/scala/io/github/arainko/tooling/Metainformation.scala index 81db87d6..51ece931 100644 --- a/tooling/src/main/scala/io/github/arainko/tooling/FullName.scala +++ b/tooling/src/main/scala/io/github/arainko/tooling/Metainformation.scala @@ -1,12 +1,12 @@ -package io.github.arainko.tooling +package io.github.arainko.ducktape.internal import scala.quoted.* -private[arainko] opaque type FullName <: String = String +private[ducktape] opaque type Metainformation <: String = String -private[arainko] object FullName { +private[ducktape] object Metainformation { - inline given derived(using DummyImplicit): FullName = ${ ownerMacro } + inline given derived(using DummyImplicit): Metainformation = ${ ownerMacro } private def ownerMacro(using Quotes) = { import quotes.reflect.* @@ -15,7 +15,7 @@ private[arainko] object FullName { val sourceFile = s"${pos.sourceFile.name}:${pos.startLine + 1}" - val rendered = + val closestOwner = List .unfold(Symbol.spliceOwner)(sym => Option.when(!sym.isNoSymbol)(sym -> sym.maybeOwner)) .collect { @@ -25,6 +25,6 @@ private[arainko] object FullName { .mkString(".") .replace("$", "") - '{ ${ Expr(s"$rendered @ $sourceFile") }: FullName } + '{ ${ Expr(s"$closestOwner @ $sourceFile") }: Metainformation } } }