Skip to content

Commit

Permalink
change TransformationSite to be a union of strings from the outside n…
Browse files Browse the repository at this point in the history
…ot to introduce another type to the public API
  • Loading branch information
arainko committed Nov 25, 2023
1 parent 21d783f commit 9db055e
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.github.arainko.ducktape

import io.github.arainko.ducktape.internal.{Transformations, TransformationSite}
import io.github.arainko.ducktape.internal.{Transformations}

final class AppliedBuilder[Source, Dest](value: Source) {
inline def transform(inline config: Field[Source, Dest] | Case[Source, Dest]*): Dest =
Transformations.between[Source, Dest](value, TransformationSite.Transformation, config*)
Transformations.between[Source, Dest](value, "transformation", config*)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package io.github.arainko.ducktape

import io.github.arainko.ducktape.internal.{Transformations, TransformationSite}
import io.github.arainko.ducktape.internal.Transformations

final class AppliedViaBuilder[Source, Dest, Func, Args <: FunctionArguments] private (value: Source, function: Func) {
inline def transform(inline config: Field[Source, Args] | Case[Source, Args]*): Dest =
Transformations.via[Source, Dest, Func, Args](value, function, TransformationSite.Transformation, config*)
Transformations.via[Source, Dest, Func, Args](value, function, "transformation", config*)
}

object AppliedViaBuilder {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.github.arainko.ducktape

import io.github.arainko.ducktape.internal.{Transformations, TransformationSite}
import io.github.arainko.ducktape.internal.Transformations

final class DefinitionBuilder[Source, Dest] {
inline def build(inline config: Field[Source, Dest] | Case[Source, Dest]*): Transformer[Source, Dest] = source =>
Transformations.between[Source, Dest](source, TransformationSite.Definition, config*)
Transformations.between[Source, Dest](source, "definition", config*)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package io.github.arainko.ducktape

import io.github.arainko.ducktape.internal.{ TransformationSite, Transformations }
import io.github.arainko.ducktape.internal.Transformations

final class DefinitionViaBuilder[Source, Dest, Func, Args <: FunctionArguments] private (function: Func) {
transparent inline def build(inline config: Field[Source, Args] | Case[Source, Args]*): Transformer[Source, Dest] =
new Transformer[Source, Dest] {
def transform(value: Source): Dest =
Transformations.via[Source, Dest, Func, Args](value, function, TransformationSite.Definition, config*)
Transformations.via[Source, Dest, Func, Args](value, function, "definition", config*)
}
}

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

import io.github.arainko.ducktape.DefinitionViaBuilder.PartiallyApplied
import io.github.arainko.ducktape.internal.{Transformations, TransformationSite}
import io.github.arainko.ducktape.internal.Transformations

trait Transformer[Source, Dest] extends Transformer.Derived[Source, Dest]

object Transformer {
inline given derive[Source, Dest]: Transformer.Derived[Source, Dest] = new {
def transform(value: Source): Dest = Transformations.between[Source, Dest](value, TransformationSite.Definition)
def transform(value: Source): Dest = Transformations.between[Source, Dest](value, "definition")
}

def define[Source, Dest]: DefinitionBuilder[Source, Dest] =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package io.github.arainko.ducktape.internal

import scala.quoted.FromExpr
import scala.quoted.Expr
import scala.quoted.Quotes
import scala.quoted.*

enum TransformationSite {
private[ducktape] enum TransformationSite {
case Definition
case Transformation
}

object TransformationSite {
given fromExpr: FromExpr[TransformationSite] =
new {
def unapply(x: Expr[TransformationSite])(using Quotes): Option[TransformationSite] =
x match {
case '{ TransformationSite.Definition } => Some(TransformationSite.Definition)
case '{ TransformationSite.Transformation } => Some(TransformationSite.Transformation)
case _ => None
}
}
private[ducktape] object TransformationSite {
def fromStringExpr(value: Expr["transformation" | "definition"])(using Quotes): TransformationSite = {
import quotes.reflect.*

summon[FromExpr["transformation" | "definition"]].unapply(value).map {
case "transformation" => TransformationSite.Transformation
case "definition" => TransformationSite.Definition
}.getOrElse(report.errorAndAbort("Couldn't parse TransformationSite from a literal string", value))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import scala.quoted.runtime.StopMacroExpansion
private[ducktape] object Transformations {
inline def between[A, B](
value: A,
inline transformationSite: TransformationSite,
inline transformationSite: "transformation" | "definition",
inline configs: Field[A, B] | Case[A, B]*
): B = ${ createTransformationBetween[A, B]('value, 'transformationSite, 'configs) }

private def createTransformationBetween[A: Type, B: Type](
value: Expr[A],
transformationSite: Expr[TransformationSite],
transformationSite: Expr["transformation" | "definition"],
configs: Expr[Seq[Field[A, B] | Case[A, B]]]
)(using Quotes): Expr[B] = {
given TransformationSite = transformationSite.valueOrAbort
given TransformationSite = TransformationSite.fromStringExpr(transformationSite)
val plan = Planner.between(Structure.of[A], Structure.of[B])
val config = Configuration.parse(configs)
createTransformation(value, plan, config).asExprOf[B]
Expand All @@ -27,24 +27,24 @@ private[ducktape] object Transformations {
inline def via[A, B, Func, Args <: FunctionArguments](
value: A,
function: Func,
inline transformationSite: TransformationSite,
inline transformationSite: "transformation" | "definition",
inline configs: Field[A, Args] | Case[A, Args]*
): B = ${ createTransformationVia[A, B, Func, Args]('value, 'function, 'transformationSite, 'configs) }

transparent inline def viaInferred[A, Func, Args <: FunctionArguments](
value: A,
inline transformationSite: TransformationSite,
inline transformationSite: "transformation" | "definition",
inline function: Func,
inline configs: Field[A, Args] | Case[A, Args]*
): Any = ${ createTransformationViaInferred('value, 'function, 'transformationSite, 'configs) }

private def createTransformationViaInferred[A: Type, Func: Type, Args <: FunctionArguments: Type](
value: Expr[A],
function: Expr[Func],
transformationSite: Expr[TransformationSite],
transformationSite: Expr["transformation" | "definition"],
configs: Expr[Seq[Field[A, Args] | Case[A, Args]]]
)(using Quotes) = {
given TransformationSite = transformationSite.valueOrAbort
given TransformationSite = TransformationSite.fromStringExpr(transformationSite)

val plan =
Function
Expand All @@ -68,10 +68,10 @@ private[ducktape] object Transformations {
private def createTransformationVia[A: Type, B: Type, Func: Type, Args <: FunctionArguments: Type](
value: Expr[A],
function: Expr[Func],
transformationSite: Expr[TransformationSite],
transformationSite: Expr["transformation" | "definition"],
configs: Expr[Seq[Field[A, Args] | Case[A, Args]]]
)(using Quotes) = {
given TransformationSite = transformationSite.valueOrAbort
given TransformationSite = TransformationSite.fromStringExpr(transformationSite)

val plan =
Function
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.github.arainko.ducktape

import io.github.arainko.ducktape.internal.{Transformations, TransformationSite}
import io.github.arainko.ducktape.internal.Transformations

extension [Source](source: Source) {
inline def to[Dest]: Dest = Transformations.between[Source, Dest](source, TransformationSite.Transformation)
inline def to[Dest]: Dest = Transformations.between[Source, Dest](source, "transformation")

def into[Dest]: AppliedBuilder[Source, Dest] = AppliedBuilder[Source, Dest](source)

transparent inline def via[Func](inline function: Func): Any =
Transformations.viaInferred[Source, Func, Nothing](source, TransformationSite.Transformation, function)
Transformations.viaInferred[Source, Func, Nothing](source, "transformation", function)

transparent inline def intoVia[Func](inline function: Func): Any =
AppliedViaBuilder.create(source, function)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.arainko.ducktape.total

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

import scala.annotation.nowarn

Expand Down Expand Up @@ -409,4 +410,57 @@ class NestedConfigurationSuite extends DucktapeSuite {
"Configuration is not valid since the provided type (123) is not a subtype of DestLevel3 @ SourceToplevel1.at[SourceToplevel1.Level1].level2.at[SourceLevel2.Level2].level3.at[SourceLevel3.Extra]"
)
}: @nowarn("msg=unused local definition")

test("Case.computed works for nested cases") {
enum SourceToplevel1 {
case Level1(level2: SourceLevel2)
}

enum SourceLevel2 {
case Level2(level3: SourceLevel3)
}

enum SourceLevel3 {
case One(int: Int)
case Two(str: String)
case Extra(int: Int)
}

enum DestToplevel1 {
case Level1(level2: DestLevel2)
}

enum DestLevel2 {
case Level2(level3: DestLevel3)
}

enum DestLevel3 {
case One(int: Int)
case Two(str: String)
}

val source = SourceToplevel1.Level1(SourceLevel2.Level2(SourceLevel3.Extra(1)))
val expected = DestToplevel1.Level1(DestLevel2.Level2(DestLevel3.One(6)))

assertEachEquals(
source
.into[DestToplevel1]
.transform(
Case.computed(
_.at[SourceToplevel1.Level1].level2.at[SourceLevel2.Level2].level3.at[SourceLevel3.Extra],
extra => DestLevel3.One(extra.int + 5)
)
),
Transformer
.define[SourceToplevel1, DestToplevel1]
.build(
Case.computed(
_.at[SourceToplevel1.Level1].level2.at[SourceLevel2.Level2].level3.at[SourceLevel3.Extra],
extra => DestLevel3.One(extra.int + 5)
)
)
.transform(source)
)(expected)

}
}

0 comments on commit 9db055e

Please sign in to comment.