Skip to content

Commit

Permalink
add @compileTimeOnly where applicable, delete instance values in Bu…
Browse files Browse the repository at this point in the history
…ilderConfig/ArgBuilderConfig, introduce NotQuotedException, add scalafix, hide all the internal classes, improve the 3.2.0 regression workaround
  • Loading branch information
arainko committed Sep 25, 2022
1 parent b94e276 commit 3c42e07
Show file tree
Hide file tree
Showing 27 changed files with 126 additions and 107 deletions.
10 changes: 10 additions & 0 deletions .scalafix.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rules = [
OrganizeImports
]

OrganizeImports {
coalesceToWildcardImportThreshold = 3
groupedImports = AggressiveMerge

removeUnused = false
}
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ If this project interests you, please drop a 🌟 - these things are worthless b

### Installation
```scala
libraryDependencies += "io.github.arainko" %% "ducktape" % "0.1.0-RC1"
libraryDependencies += "io.github.arainko" %% "ducktape" % "0.1.0-RC2"
```

### Examples
Expand Down Expand Up @@ -322,13 +322,13 @@ val definedViaTransformer =
Transformer
.defineVia[TestClass](method)
.build(Arg.const(_.additionalArg, List("const")))
// definedViaTransformer: Transformer[TestClass, TestClassWithAdditionalList] = repl.MdocSession$MdocApp6$$Lambda$8962/0x0000000802e7e040@61870c88
// definedViaTransformer: Transformer[TestClass, TestClassWithAdditionalList] = repl.MdocSession$MdocApp6$$Lambda$41195/0x000000010897d840@6ee2238f

val definedTransformer =
Transformer
.define[TestClass, TestClassWithAdditionalList]
.build(Field.const(_.additionalArg, List("const")))
// definedTransformer: Transformer[TestClass, TestClassWithAdditionalList] = repl.MdocSession$MdocApp6$$Lambda$8963/0x0000000802e7e440@2f13a6ac
// definedTransformer: Transformer[TestClass, TestClassWithAdditionalList] = repl.MdocSession$MdocApp6$$Lambda$41196/0x000000010897dc40@3eb47bb1

val transformedVia = definedViaTransformer.transform(testClass)
// transformedVia: TestClassWithAdditionalList = TestClassWithAdditionalList(
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ThisBuild / developers := List(
)
)
ThisBuild / scalaVersion := "3.2.0"
ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.6.0"

name := "ducktape"
sonatypeRepository := "https://s01.oss.sonatype.org/service/local"
Expand All @@ -29,7 +30,7 @@ lazy val ducktape =
project
.in(file("ducktape"))
.settings(
scalacOptions ++= List("-Xcheck-macros", "-no-indent", "-old-syntax"),
scalacOptions ++= List("-Xcheck-macros", "-no-indent", "-old-syntax", "-Xfatal-warnings"),
libraryDependencies += "org.scalameta" %% "munit" % "1.0.0-M6" % Test
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,43 @@
package io.github.arainko.ducktape

import io.github.arainko.ducktape.function.FunctionArguments
import io.github.arainko.ducktape.internal.NotQuotedException

import scala.annotation.{ compileTimeOnly, implicitNotFound }
import scala.deriving.Mirror
import scala.annotation.implicitNotFound
import scala.annotation.compileTimeOnly

opaque type ArgBuilderConfig[Source, Dest, ArgSelector <: FunctionArguments] = Unit

object ArgBuilderConfig {
private[ducktape] def instance[Source, Dest, ArgSelector <: FunctionArguments]: ArgBuilderConfig[Source, Dest, ArgSelector] = ()
}

//TODO: Slap a @compileTimeOnly on all things here
object Arg {

@compileTimeOnly("'Arg.const' needs to be erased from the AST with a macro.")
def const[Source, Dest, ArgType, ActualType, ArgSelector <: FunctionArguments](
selector: ArgSelector => ArgType,
const: ActualType
)(using
@implicitNotFound("Arg.const is only supported for product types but ${Source} is not a product type.")
ev1: Mirror.ProductOf[Source],
ev2: ActualType <:< ArgType
): ArgBuilderConfig[Source, Dest, ArgSelector] = ArgBuilderConfig.instance
): ArgBuilderConfig[Source, Dest, ArgSelector] = throw NotQuotedException("Arg.const")

@compileTimeOnly("'Arg.computed' needs to be erased from the AST with a macro.")
def computed[Source, Dest, ArgType, ActualType, ArgSelector <: FunctionArguments](
selector: ArgSelector => ArgType,
f: Source => ActualType
)(using
@implicitNotFound("Arg.computed is only supported for product types but ${Source} is not a product type.")
ev1: Mirror.ProductOf[Source],
ev2: ActualType <:< ArgType
): ArgBuilderConfig[Source, Dest, ArgSelector] = ArgBuilderConfig.instance
): ArgBuilderConfig[Source, Dest, ArgSelector] = throw NotQuotedException("Arg.computed")

@compileTimeOnly("'Arg.renamed' needs to be erased from the AST with a macro.")
def renamed[Source, Dest, ArgType, FieldType, ArgSelector <: FunctionArguments](
destSelector: ArgSelector => ArgType,
sourceSelector: Source => FieldType,
sourceSelector: Source => FieldType
)(using
@implicitNotFound("Arg.renamed is only supported for product types but ${Source} is not a product type.")
ev1: Mirror.ProductOf[Source],
ev2: FieldType <:< ArgType
): ArgBuilderConfig[Source, Dest, ArgSelector] = ArgBuilderConfig.instance
): ArgBuilderConfig[Source, Dest, ArgSelector] = throw NotQuotedException("Arg.renamed")

}
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
package io.github.arainko.ducktape

import io.github.arainko.ducktape.internal.NotQuotedException

import scala.annotation.{ compileTimeOnly, implicitNotFound }
import scala.deriving.Mirror
import scala.util.NotGiven
import scala.annotation.implicitNotFound

opaque type BuilderConfig[Source, Dest] = Unit

object BuilderConfig {
private[ducktape] def instance[Source, Dest]: BuilderConfig[Source, Dest] = ()
}

//TODO: Slap a @compileTimeOnly on all things here
object Field {

@compileTimeOnly("'Field.const' needs to be erased from the AST with a macro.")
def const[Source, Dest, FieldType, ActualType](selector: Dest => FieldType, value: ActualType)(using
ev1: ActualType <:< FieldType,
@implicitNotFound("Field.const is supported for product types only, but ${Source} is not a product type.")
ev2: Mirror.ProductOf[Source],
@implicitNotFound("Field.const is supported for product types only, but ${Dest} is not a product type.")
ev3: Mirror.ProductOf[Dest]
): BuilderConfig[Source, Dest] = BuilderConfig.instance
): BuilderConfig[Source, Dest] = throw NotQuotedException("Field.const")

@compileTimeOnly("'Field.computed' needs to be erased from the AST with a macro.")
def computed[Source, Dest, FieldType, ActualType](selector: Dest => FieldType, f: Source => ActualType)(using
ev1: ActualType <:< FieldType,
@implicitNotFound("Field.computed is supported for product types only, but ${Source} is not a product type.")
ev2: Mirror.ProductOf[Source],
@implicitNotFound("Field.computed is supported for product types only, but ${Dest} is not a product type.")
ev3: Mirror.ProductOf[Dest]
): BuilderConfig[Source, Dest] = BuilderConfig.instance
): BuilderConfig[Source, Dest] = throw NotQuotedException("Field.computed")

@compileTimeOnly("'Field.renamed' needs to be erased from the AST with a macro.")
def renamed[Source, Dest, SourceFieldType, DestFieldType](
destSelector: Dest => DestFieldType,
sourceSelector: Source => SourceFieldType
Expand All @@ -37,44 +38,47 @@ object Field {
ev2: Mirror.ProductOf[Source],
@implicitNotFound("Field.renamed is supported for product types only, but ${Dest} is not a product type.")
ev3: Mirror.ProductOf[Dest]
): BuilderConfig[Source, Dest] = BuilderConfig.instance
): BuilderConfig[Source, Dest] = throw NotQuotedException("Field.renamed")
}

//TODO: Slap a @compileTimeOnly on all things here
object Case {
def const[SourceSubtype]: Case.Const[SourceSubtype] = Const.instance

def computed[SourceSubtype]: Case.Computed[SourceSubtype] = Computed.instance
@compileTimeOnly("'Case.const' needs to be erased from the AST with a macro.")
def const[SourceSubtype]: Case.Const[SourceSubtype] = throw NotQuotedException("Case.const")

@compileTimeOnly("'Case.computed' needs to be erased from the AST with a macro.")
def computed[SourceSubtype]: Case.Computed[SourceSubtype] = throw NotQuotedException("Case.computed")

opaque type Computed[SourceSubtype] = Unit

object Computed {
private[ducktape] def instance[SourceSubtype]: Computed[SourceSubtype] = ()

extension [SourceSubtype](inst: Computed[SourceSubtype]) {

@compileTimeOnly("'Case.computed' needs to be erased from the AST with a macro.")
def apply[Source, Dest](f: SourceSubtype => Dest)(using
@implicitNotFound("Case.computed is only supported for coproducts but ${Source} is not a coproduct.")
ev1: Mirror.SumOf[Source],
ev2: SourceSubtype <:< Source,
@implicitNotFound("Case.computed is only supported for subtypes of ${Source}.")
ev3: NotGiven[SourceSubtype =:= Source]
): BuilderConfig[Source, Dest] = BuilderConfig.instance
): BuilderConfig[Source, Dest] = throw NotQuotedException("Case.computed")
}
}

opaque type Const[SourceSubtype] = Unit

object Const {
private[ducktape] def instance[SourceSubtype]: Const[SourceSubtype] = ()

extension [SourceSubtype](inst: Const[SourceSubtype]) {

@compileTimeOnly("'Case.const' needs to be erased from the AST with a macro.")
def apply[Source, Dest](const: Dest)(using
@implicitNotFound("Case.computed is only supported for coproducts but ${Source} is not a coproduct.")
ev1: Mirror.SumOf[Source],
ev2: SourceSubtype <:< Source,
@implicitNotFound("Case.instance is only supported for subtypes of ${Source}.")
ev3: NotGiven[SourceSubtype =:= Source]
): BuilderConfig[Source, Dest] = BuilderConfig.instance
): BuilderConfig[Source, Dest] = throw NotQuotedException("Case.const")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ package io.github.arainko.ducktape
import io.github.arainko.ducktape.builder.*
import io.github.arainko.ducktape.internal.macros.*

import scala.collection.BuildFrom
import scala.collection.Factory
import scala.collection.{ BuildFrom, Factory }
import scala.compiletime.*
import scala.deriving.Mirror

Expand All @@ -20,12 +19,12 @@ object Transformer {

def defineVia[A]: DefinitionViaBuilder.PartiallyApplied[A] = DefinitionViaBuilder.create[A]

sealed trait Identity[Source] extends Transformer[Source, Source]

given [Source]: Identity[Source] = new {
final class Identity[Source] private[Transformer] extends Transformer[Source, Source] {
def transform(from: Source): Source = from
}

given [Source]: Identity[Source] = Identity[Source]

inline given forProducts[Source, Dest](using Mirror.ProductOf[Source], Mirror.ProductOf[Dest]): Transformer[Source, Dest] =
from => ProductTransformerMacros.transform(from)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package io.github.arainko.ducktape.builder

import io.github.arainko.ducktape.*
import io.github.arainko.ducktape.internal.macros.*
import scala.deriving.Mirror
import io.github.arainko.ducktape.function.*
import io.github.arainko.ducktape.internal.macros.*

import scala.compiletime.*
import scala.deriving.Mirror

sealed abstract class AppliedViaBuilder[Source, Dest, Func, ArgSelector <: FunctionArguments](
final class AppliedViaBuilder[Source, Dest, Func, ArgSelector <: FunctionArguments] private (
source: Source,
function: Func
) {
Expand All @@ -18,14 +19,13 @@ sealed abstract class AppliedViaBuilder[Source, Dest, Func, ArgSelector <: Funct
}

object AppliedViaBuilder {
private[AppliedViaBuilder] class Impl[Source, Dest, Func, ArgSelector <: FunctionArguments](
private def instance[Source, Dest, Func, ArgSelector <: FunctionArguments](
source: Source,
function: Func
) extends AppliedViaBuilder[Source, Dest, Func, ArgSelector](source, function)
) = AppliedViaBuilder[Source, Dest, Func, ArgSelector](source, function)

transparent inline def create[Source, Func](source: Source, inline func: Func)(using Func: FunctionMirror[Func]) = {
// widen the type to not infer `AppliedViaBuilder.Impl`, we're in a transparent inline method after all
val builder: AppliedViaBuilder[Source, Func.Return, Func, Nothing] = Impl(source, func)
FunctionMacros.namedArguments(func, builder)
val builder = instance[Source, Func.Return, Func, Nothing](source, func)
FunctionMacros.namedArguments(func, instance[Source, Func.Return, Func, Nothing](source, func))
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package io.github.arainko.ducktape.builder

import io.github.arainko.ducktape.*
import io.github.arainko.ducktape.function.*
import io.github.arainko.ducktape.internal.macros.*

import scala.deriving.*
import io.github.arainko.ducktape.function.*

sealed abstract class DefinitionViaBuilder[Source, Dest, Func, ArgSelector <: FunctionArguments](function: Func) {
final class DefinitionViaBuilder[Source, Dest, Func, ArgSelector <: FunctionArguments] private (function: Func) {

inline def build(
inline config: ArgBuilderConfig[Source, Dest, ArgSelector]*
Expand All @@ -14,9 +15,8 @@ sealed abstract class DefinitionViaBuilder[Source, Dest, Func, ArgSelector <: Fu
}

object DefinitionViaBuilder {
private[DefinitionViaBuilder] class Impl[Source, Dest, Func, ArgSelector <: FunctionArguments](
function: Func
) extends DefinitionViaBuilder[Source, Dest, Func, ArgSelector](function)
private def instance[Source, Dest, Func, ArgSelector <: FunctionArguments](function: Func) =
DefinitionViaBuilder[Source, Dest, Func, ArgSelector](function)

def create[Source]: PartiallyApplied[Source] = ()

Expand All @@ -25,8 +25,7 @@ object DefinitionViaBuilder {
object PartiallyApplied {
extension [Source](partial: PartiallyApplied[Source]) {
transparent inline def apply[Func](inline func: Func)(using Func: FunctionMirror[Func]) = {
// widen the type to not infer `DefinitionViaBuilder.Impl`, we're in a transparent inline method after all
val builder: DefinitionViaBuilder[Source, Func.Return, Func, Nothing] = Impl(func)
val builder = instance[Source, Func.Return, Func, Nothing](func)
FunctionMacros.namedArguments(func, builder)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.arainko.ducktape.function

import io.github.arainko.ducktape.internal.macros.*

import scala.annotation.implicitNotFound

@implicitNotFound("FunctionMirrors are only available for function types, but got ${F}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.arainko.ducktape.internal

private[ducktape] final class NotQuotedException(name: String)
extends Exception(
s"""
|'$name' was not lifted away from the AST with a macro but also skirted past the compiler into the runtime.
|This is not good.
|Please file an issue on the 'ducktape' repository.""".stripMargin
)
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package io.github.arainko.ducktape.internal.macros

import scala.quoted.*
import io.github.arainko.ducktape.*
import io.github.arainko.ducktape.internal.modules.*

import scala.deriving.*
import scala.quoted.*

private[ducktape] class CoproductTransformerMacros(using val quotes: Quotes)
extends Module,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package io.github.arainko.ducktape.internal.macros

import scala.quoted.*
import io.github.arainko.ducktape.*

object DebugMacros {
import scala.quoted.*

private[ducktape] object DebugMacros {
inline def structure[A](inline value: A) = ${ structureMacro('value) }

def structureMacro[A: Type](value: Expr[A])(using Quotes) = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package io.github.arainko.ducktape.internal.macros

import scala.quoted.*
import io.github.arainko.ducktape.internal.modules.*
import io.github.arainko.ducktape.*
import io.github.arainko.ducktape.function.*
import io.github.arainko.ducktape.internal.modules.*

import scala.deriving.*
import scala.quoted.*

private[ducktape] class ProductTransformerMacros(using val quotes: Quotes)
extends Module,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private[ducktape] final class TransformerMacros(using val quotes: Quotes) extend

}

object TransformerMacros {
private[ducktape] object TransformerMacros {

inline def transformConfigured[Source, Dest](
sourceValue: Source,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.github.arainko.ducktape.internal.modules

import io.github.arainko.ducktape.{ Case => CaseConfig, Field => FieldConfig, _ }
import io.github.arainko.ducktape.function.FunctionArguments
import io.github.arainko.ducktape.{ Case => CaseConfig, Field => FieldConfig, _ }

import scala.quoted.*

private[internal] trait ConfigurationModule { self: Module & SelectorModule & MirrorModule & FieldModule & CaseModule =>
private[ducktape] trait ConfigurationModule { self: Module & SelectorModule & MirrorModule & FieldModule & CaseModule =>
import quotes.reflect.*

sealed trait MaterializedConfiguration
Expand Down
Loading

0 comments on commit 3c42e07

Please sign in to comment.