Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better lens-like operations support #583

Merged
merged 9 commits into from
Aug 14, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ private[compiletime] trait TypesPlatform extends Types { this: DefinitionsPlatfo
.toMap
typeArgumentByName
// unknown
// $COVERAGE-OFF$should never happen unless we messed up
case out =>
// $COVERAGE-OFF$should never happen unless we messed up
assertionFailed(
s"Constructor of ${Type.prettyPrint(fromUntyped[Any](tpe))} has unrecognized/unsupported format of type: $out"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ trait SingletonTypes { this: (Definitions & ProductTypes) =>
case _ if ProductType.isCaseObject[A] || ProductType.isCaseVal[A] || ProductType.isJavaEnumValue[A] =>
Type[A] match {
case Product.Constructor(params, ctor) if params.isEmpty => found(ctor(Map.empty))
case _ =>
// $COVERAGE-OFF$should never happen unless we messed up
// $COVERAGE-OFF$should never happen unless we messed up
case _ =>
assertionFailed(
s"Expected case object/case with no params/Java enum of ${Type.prettyPrint[A]} to have a nullary constructor"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,17 @@ private[compiletime] trait Configurations { this: Derivation =>
case ChimneyType.PatcherFlags.Disable(flag, flags) =>
import flag.Underlying as Flag, flags.Underlying as Flags2
extractTransformerFlags[Flags2](defaultFlags).setBoolFlag[Flag](value = false)
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
case _ =>
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
reportError(s"Invalid internal PatcherFlags type shape: ${Type.prettyPrint[Flags]}!")
// $COVERAGE-ON$
}

private def extractPatcherConfig[Tail <: runtime.PatcherOverrides: Type](): PatcherConfiguration =
Type[Tail] match {
case empty if empty =:= ChimneyType.PatcherOverrides.Empty => PatcherConfiguration()
case _ =>
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
case _ =>
reportError(s"Invalid internal PatcherOverrides type shape: ${Type.prettyPrint[Tail]}!!")
// $COVERAGE-ON$
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ private[compiletime] trait Derivation
transformedExpr.orElse(targetGetter.value.get(ctx.obj).upcastToExprOf[Option[TargetInner]]).as_??
)
}
// $COVERAGE-OFF$should never happen unless we messed up
case _ =>
// $COVERAGE-OFF$should never happen unless we messed up
assertionFailed(
s"Expected both types to be options, got ${Type.prettyPrint[PatchGetter]} and ${Type.prettyPrint[TargetParam]}"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import scala.collection.immutable.{ListMap, ListSet}

private[compiletime] trait Configurations { this: Derivation =>

import Type.Implicits.*

final protected case class TransformerFlags(
inheritedAccessors: Boolean = false,
methodAccessors: Boolean = false,
Expand Down Expand Up @@ -308,6 +310,7 @@ private[compiletime] trait Configurations { this: Derivation =>
runtimeOverrides.isEmpty
def areLocalFlagsAndOverridesEmpty: Boolean =
areLocalFlagsEmpty && areOverridesEmpty

def filterCurrentOverridesForField(nameFilter: String => Boolean): Map[String, TransformerOverride.ForField] =
ListMap.from(
runtimeOverridesForCurrent.collect {
Expand All @@ -318,14 +321,52 @@ private[compiletime] trait Configurations { this: Derivation =>
def filterCurrentOverridesForSubtype(
sourceTypeFilter: ?? => Boolean,
@scala.annotation.unused targetTypeFilter: ?? => Boolean
): Map[Option[??], TransformerOverride.ForSubtype] =
ListMap.from(
runtimeOverridesForCurrent.collect {
case (Path.AtSourceSubtype(tpe, _), runtimeCoproductOverride: TransformerOverride.ForSubtype)
if sourceTypeFilter(tpe) =>
Some(tpe) -> runtimeCoproductOverride
}
)
): Map[Option[??], TransformerOverride.ForSubtype] = ListMap.from(
runtimeOverridesForCurrent.collect {
case (Path.AtSourceSubtype(tpe, _), runtimeCoproductOverride: TransformerOverride.ForSubtype)
if sourceTypeFilter(tpe) =>
Some(tpe) -> runtimeCoproductOverride
}
)
def filterCurrentOverridesForSome: Set[TransformerOverride.ForField] = ListSet.from(
runtimeOverrides.collect {
case (Path.AtSubtype(tpe, path), runtimeFieldOverride: TransformerOverride.ForField)
if path == Path.Root && tpe.Underlying <:< Type[Some[Any]] =>
runtimeFieldOverride
}
)
def filterCurrentOverridesForLeft: Set[TransformerOverride.ForField] = ListSet.from(
runtimeOverrides.collect {
case (Path.AtSubtype(tpe, path), runtimeFieldOverride: TransformerOverride.ForField)
if path == Path.Root && tpe.Underlying <:< Type[Left[Any, Any]] =>
runtimeFieldOverride
}
)
def filterCurrentOverridesForRight: Set[TransformerOverride.ForField] = ListSet.from(
runtimeOverrides.collect {
case (Path.AtSubtype(tpe, path), runtimeFieldOverride: TransformerOverride.ForField)
if path == Path.Root && tpe.Underlying <:< Type[Right[Any, Any]] =>
runtimeFieldOverride
}
)
def filterCurrentOverridesForEveryItem: Set[TransformerOverride.ForField] = ListSet.from(
runtimeOverrides.collect {
case (Path.AtItem(path), runtimeFieldOverride: TransformerOverride.ForField) if path == Path.Root =>
runtimeFieldOverride
}
)
def filterCurrentOverridesForEveryMapKey: Set[TransformerOverride.ForField] = ListSet.from(
runtimeOverrides.collect {
case (Path.AtMapKey(path), runtimeFieldOverride: TransformerOverride.ForField) if path == Path.Root =>
runtimeFieldOverride
}
)
def filterCurrentOverridesForEveryMapValue: Set[TransformerOverride.ForField] = ListSet.from(
runtimeOverrides.collect {
case (Path.AtMapValue(path), runtimeFieldOverride: TransformerOverride.ForField) if path == Path.Root =>
runtimeFieldOverride
}
)
def currentOverrideForConstructor: Option[TransformerOverride.ForConstructor] =
runtimeOverridesForCurrent.collectFirst {
case (_, runtimeConstructorOverride: TransformerOverride.ForConstructor) => runtimeConstructorOverride
Expand Down Expand Up @@ -446,8 +487,8 @@ private[compiletime] trait Configurations { this: Derivation =>
case _ =>
extractTransformerFlags[Flags2](defaultFlags).setBoolFlag[Flag](value = false)
}
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
case _ =>
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
reportError(s"Invalid internal TransformerFlags type shape: ${Type.prettyPrint[Flags]}!")
// $COVERAGE-ON$
}
Expand Down Expand Up @@ -527,8 +568,8 @@ private[compiletime] trait Configurations { this: Derivation =>
extractPath[FromPath],
TransformerOverride.RenamedTo(extractPath[ToPath])
)
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
case _ =>
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
reportError(s"Invalid internal TransformerOverrides type shape: ${Type.prettyPrint[Tail]}!!")
// $COVERAGE-ON$
}
Expand All @@ -554,8 +595,8 @@ private[compiletime] trait Configurations { this: Derivation =>
case ChimneyType.Path.EveryMapValue(init) =>
import init.Underlying as PathType2
extractPath[PathType2].everyMapValue
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
case _ =>
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
reportError(s"Invalid internal Path shape: ${Type.prettyPrint[PathType]}!!")
// $COVERAGE-ON$
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import io.scalaland.chimney.internal.compiletime.DerivationResult
import io.scalaland.chimney.internal.compiletime.derivation.transformer.Derivation
import io.scalaland.chimney.partial

private[compiletime] trait TransformEitherToEitherRuleModule { this: Derivation =>
private[compiletime] trait TransformEitherToEitherRuleModule {
this: Derivation & TransformProductToProductRuleModule =>

import Type.Implicits.*, ChimneyType.Implicits.*
import Type.Implicits.*, ChimneyType.Implicits.*, TransformProductToProductRule.useOverrideIfPresentOr

protected object TransformEitherToEitherRule extends Rule("EitherToEither") {

Expand All @@ -27,40 +28,50 @@ private[compiletime] trait TransformEitherToEitherRuleModule { this: Derivation
private def mapLeft[From, To, FromL: Type, FromR: Type, ToL: Type, ToR: Type](implicit
ctx: TransformationContext[From, To]
): DerivationResult[Rule.ExpansionResult[To]] =
deriveRecursiveTransformationExpr[FromL, ToL](
ctx.src.upcastToExprOf[Left[FromL, FromR]].value,
Path.Root.matching[Left[ToL, ToR]]
).flatMap { (derivedToL: TransformationExpr[ToL]) =>
// We're constructing:
// '{ Left( ${ derivedToL } ) /* from ${ src }.value */ }
DerivationResult.expanded(derivedToL.map(Expr.Either.Left[ToL, ToR](_).upcastToExprOf[To]))
useOverrideIfPresentOr("matchingLeft", ctx.config.filterCurrentOverridesForLeft) {
deriveRecursiveTransformationExpr[FromL, ToL](
ctx.src.upcastToExprOf[Left[FromL, FromR]].value,
Path.Root.matching[Left[ToL, ToR]]
)
}
.flatMap { (derivedToL: TransformationExpr[ToL]) =>
// We're constructing:
// '{ Left( ${ derivedToL } ) /* from ${ src }.value */ }
DerivationResult.expanded(derivedToL.map(Expr.Either.Left[ToL, ToR](_).upcastToExprOf[To]))
}

private def mapRight[From, To, FromL: Type, FromR: Type, ToL: Type, ToR: Type](implicit
ctx: TransformationContext[From, To]
): DerivationResult[Rule.ExpansionResult[To]] =
deriveRecursiveTransformationExpr[FromR, ToR](
ctx.src.upcastToExprOf[Right[FromL, FromR]].value,
Path.Root.matching[Right[ToL, ToR]]
).flatMap { (derivedToR: TransformationExpr[ToR]) =>
// We're constructing:
// '{ Right( ${ derivedToR } ) /* from ${ src }.value */ }
DerivationResult.expanded(derivedToR.map(Expr.Either.Right[ToL, ToR](_).upcastToExprOf[To]))
useOverrideIfPresentOr("matchingRight", ctx.config.filterCurrentOverridesForRight) {
deriveRecursiveTransformationExpr[FromR, ToR](
ctx.src.upcastToExprOf[Right[FromL, FromR]].value,
Path.Root.matching[Right[ToL, ToR]]
)
}
.flatMap { (derivedToR: TransformationExpr[ToR]) =>
// We're constructing:
// '{ Right( ${ derivedToR } ) /* from ${ src }.value */ }
DerivationResult.expanded(derivedToR.map(Expr.Either.Right[ToL, ToR](_).upcastToExprOf[To]))
}

private def mapEither[From, To, FromL: Type, FromR: Type, ToL: Type, ToR: Type](implicit
ctx: TransformationContext[From, To]
): DerivationResult[Rule.ExpansionResult[To]] = {
val toLeftResult = ExprPromise
.promise[FromL](ExprPromise.NameGenerationStrategy.FromPrefix("left"))
.traverse { (leftExpr: Expr[FromL]) =>
deriveRecursiveTransformationExpr[FromL, ToL](leftExpr, Path.Root.matching[Left[ToL, ToR]])
useOverrideIfPresentOr("matchingLeft", ctx.config.filterCurrentOverridesForLeft) {
deriveRecursiveTransformationExpr[FromL, ToL](leftExpr, Path.Root.matching[Left[ToL, ToR]])
}
}

val toRightResult = ExprPromise
.promise[FromR](ExprPromise.NameGenerationStrategy.FromPrefix("right"))
.traverse { (rightExpr: Expr[FromR]) =>
deriveRecursiveTransformationExpr[FromR, ToR](rightExpr, Path.Root.matching[Right[ToL, ToR]])
useOverrideIfPresentOr("matchingRight", ctx.config.filterCurrentOverridesForRight) {
deriveRecursiveTransformationExpr[FromR, ToR](rightExpr, Path.Root.matching[Right[ToL, ToR]])
}
}

val inLeft =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import io.scalaland.chimney.partial

import scala.collection.compat.Factory

private[compiletime] trait TransformIterableToIterableRuleModule { this: Derivation =>
private[compiletime] trait TransformIterableToIterableRuleModule {
this: Derivation & TransformProductToProductRuleModule =>

import Type.Implicits.*, ChimneyType.Implicits.*
import Type.Implicits.*, ChimneyType.Implicits.*, TransformProductToProductRule.useOverrideIfPresentOr

protected object TransformIterableToIterableRule extends Rule("IterableToIterable") {

Expand Down Expand Up @@ -55,12 +56,16 @@ private[compiletime] trait TransformIterableToIterableRuleModule { this: Derivat
val toKeyResult = ExprPromise
.promise[FromK](ExprPromise.NameGenerationStrategy.FromPrefix("key"))
.traverse { key =>
deriveRecursiveTransformationExpr[FromK, ToK](key, Path.Root.everyMapKey).map(_.ensurePartial -> key)
useOverrideIfPresentOr("everyMapKey", ctx.config.filterCurrentOverridesForEveryMapKey) {
deriveRecursiveTransformationExpr[FromK, ToK](key, Path.Root.everyMapKey)
}.map(_.ensurePartial -> key)
}
val toValueResult = ExprPromise
.promise[FromV](ExprPromise.NameGenerationStrategy.FromPrefix("value"))
.traverse { value =>
deriveRecursiveTransformationExpr[FromV, ToV](value, Path.Root.everyMapValue).map(_.ensurePartial)
useOverrideIfPresentOr("everyMapValue", ctx.config.filterCurrentOverridesForEveryMapValue) {
deriveRecursiveTransformationExpr[FromV, ToV](value, Path.Root.everyMapValue)
}.map(_.ensurePartial)
}

toKeyResult.parTuple(toValueResult).flatMap { case (toKeyP, toValueP) =>
Expand Down Expand Up @@ -113,12 +118,14 @@ private[compiletime] trait TransformIterableToIterableRuleModule { this: Derivat
ExprPromise
.promise[InnerFrom](ExprPromise.NameGenerationStrategy.FromExpr(ctx.src))
.traverse { (newFromSrc: Expr[InnerFrom]) =>
deriveRecursiveTransformationExpr[InnerFrom, InnerTo](newFromSrc, Path.Root.everyItem)
useOverrideIfPresentOr("everyItem", ctx.config.filterCurrentOverridesForEveryItem) {
deriveRecursiveTransformationExpr[InnerFrom, InnerTo](newFromSrc, Path.Root.everyItem)
}
}
.flatMap { (to2P: ExprPromise[InnerFrom, TransformationExpr[InnerTo]]) =>
to2P.foldTransformationExpr { (totalP: ExprPromise[InnerFrom, Expr[InnerTo]]) =>
// TODO: restore .map implementation
if (Type[InnerFrom] =:= Type[InnerTo]) {
if (Type[InnerFrom] =:= Type[InnerTo] && ctx.config.areOverridesEmpty) {
def srcToFactory[ToOrPartialTo: Type](
factory: Expr[Factory[InnerTo, ToOrPartialTo]]
): Expr[ToOrPartialTo] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import io.scalaland.chimney.partial

import scala.collection.compat.Factory

private[compiletime] trait TransformMapToMapRuleModule { this: Derivation with TransformIterableToIterableRuleModule =>
private[compiletime] trait TransformMapToMapRuleModule {
this: Derivation & TransformIterableToIterableRuleModule & TransformProductToProductRuleModule =>

import Type.Implicits.*, ChimneyType.Implicits.*
import Type.Implicits.*, ChimneyType.Implicits.*, TransformProductToProductRule.useOverrideIfPresentOr

protected object TransformMapToMapRule extends Rule("MapToMap") {

Expand Down Expand Up @@ -74,12 +75,16 @@ private[compiletime] trait TransformMapToMapRuleModule { this: Derivation with T
val toKeyResult = ExprPromise
.promise[FromK](ExprPromise.NameGenerationStrategy.FromPrefix("key"))
.traverse { key =>
deriveRecursiveTransformationExpr[FromK, ToK](key, Path.Root.everyMapKey).map(_.ensureTotal)
useOverrideIfPresentOr("everyMapKey", ctx.config.filterCurrentOverridesForEveryMapKey) {
deriveRecursiveTransformationExpr[FromK, ToK](key, Path.Root.everyMapKey)
}.map(_.ensureTotal)
}
val toValueResult = ExprPromise
.promise[FromV](ExprPromise.NameGenerationStrategy.FromPrefix("value"))
.traverse { value =>
deriveRecursiveTransformationExpr[FromV, ToV](value, Path.Root.everyMapValue).map(_.ensureTotal)
useOverrideIfPresentOr("everyMapValue", ctx.config.filterCurrentOverridesForEveryMapValue) {
deriveRecursiveTransformationExpr[FromV, ToV](value, Path.Root.everyMapValue)
}.map(_.ensureTotal)
}

toKeyResult.parTuple(toValueResult).flatMap { case (toKeyP, toValueP) =>
Expand Down Expand Up @@ -117,12 +122,16 @@ private[compiletime] trait TransformMapToMapRuleModule { this: Derivation with T
val toKeyResult = ExprPromise
.promise[FromK](ExprPromise.NameGenerationStrategy.FromPrefix("key"))
.traverse { key =>
deriveRecursiveTransformationExpr[FromK, ToK](key, Path.Root.everyMapKey).map(_.ensurePartial -> key)
useOverrideIfPresentOr("everyMapKey", ctx.config.filterCurrentOverridesForEveryMapKey) {
deriveRecursiveTransformationExpr[FromK, ToK](key, Path.Root.everyMapKey)
}.map(_.ensurePartial -> key)
}
val toValueResult = ExprPromise
.promise[FromV](ExprPromise.NameGenerationStrategy.FromPrefix("value"))
.traverse { value =>
deriveRecursiveTransformationExpr[FromV, ToV](value, Path.Root.everyMapValue).map(_.ensurePartial -> value)
useOverrideIfPresentOr("everyMapValue", ctx.config.filterCurrentOverridesForEveryMapValue) {
deriveRecursiveTransformationExpr[FromV, ToV](value, Path.Root.everyMapValue)
}.map(_.ensurePartial -> value)
}

toKeyResult.parTuple(toValueResult).flatMap { case (toKeyP, toValueP) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import io.scalaland.chimney.internal.compiletime.DerivationResult
import io.scalaland.chimney.internal.compiletime.derivation.transformer.Derivation
import io.scalaland.chimney.partial

private[compiletime] trait TransformOptionToOptionRuleModule { this: Derivation =>
private[compiletime] trait TransformOptionToOptionRuleModule {
this: Derivation & TransformProductToProductRuleModule =>

import Type.Implicits.*, ChimneyType.Implicits.*
import Type.Implicits.*, ChimneyType.Implicits.*, TransformProductToProductRule.useOverrideIfPresentOr

protected object TransformOptionToOptionRule extends Rule("OptionToOption") {

Expand Down Expand Up @@ -36,7 +37,9 @@ private[compiletime] trait TransformOptionToOptionRuleModule { this: Derivation
ExprPromise
.promise[InnerFrom](ExprPromise.NameGenerationStrategy.FromType)
.traverse { (newFromExpr: Expr[InnerFrom]) =>
deriveRecursiveTransformationExpr[InnerFrom, InnerTo](newFromExpr, Path.Root.matching[Some[InnerTo]])
useOverrideIfPresentOr("matchingSome", ctx.config.filterCurrentOverridesForSome) {
deriveRecursiveTransformationExpr[InnerFrom, InnerTo](newFromExpr, Path.Root.matching[Some[InnerTo]])
}
}
.flatMap { (derivedToExprPromise: ExprPromise[InnerFrom, TransformationExpr[InnerTo]]) =>
derivedToExprPromise.foldTransformationExpr { (totalP: ExprPromise[InnerFrom, Expr[InnerTo]]) =>
Expand Down
Loading
Loading