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

implement requireSourceFieldsUsedExcept #608

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
Expand Up @@ -218,6 +218,19 @@ final class TransformerDefinition[From, To, Overrides <: TransformerOverrides, F
)(implicit ev: IsFunction.Of[Ctor, To]): TransformerDefinition[From, To, ? <: TransformerOverrides, Flags] =
macro TransformerDefinitionMacros.withConstructorImpl[From, To, Overrides, Flags]

/** Ignore if a source field is not used in the transformation. This can be useful when `.enableUnusedFieldPolicy` is
* enabled.
*
* @param selectorFrom
* the field is that not required to be used in the transformation
* @return
* [[io.scalaland.chimney.dsl.TransformerDefinition]]
*
* @since 1.5.0
*/
def withIgnoreUnusedField(selectorFrom: From => ?): TransformerInto[From, To, ? <: TransformerOverrides, Flags] =
macro TransformerDefinitionMacros.withIgnoreUnusedField[From, To, Overrides, Flags]

/** Build Transformer using current configuration.
*
* It runs macro that tries to derive instance of `Transformer[From, To]`. When transformation can't be derived, it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,19 @@ final class TransformerInto[From, To, Overrides <: TransformerOverrides, Flags <
): TransformerInto[From, To, ? <: TransformerOverrides, Flags] =
macro TransformerIntoMacros.withConstructorImpl[From, To, Overrides, Flags]

/** Ignore if a source field is not used in the transformation. This can be useful when `.enableUnusedFieldPolicy` is
* enabled.
*
* @param selectorFrom
* the field is that not required to be used in the transformation
* @return
* [[io.scalaland.chimney.dsl.TransformerInto]]
*
* @since 1.5.0
*/
def withIgnoreUnusedField(selectorFrom: From => ?): TransformerInto[From, To, ? <: TransformerOverrides, Flags] =
macro TransformerIntoMacros.withIgnoreUnusedField[From, To, Overrides, Flags]

/** Apply configured transformation in-place.
*
* It runs macro that tries to derive instance of `Transformer[From, To]` and immediately apply it to captured
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ private[compiletime] trait ChimneyTypesPlatform extends ChimneyTypes { this: Chi
val PreferPartialTransformer: Type[io.scalaland.chimney.dsl.PreferPartialTransformer.type] =
weakTypeTag[io.scalaland.chimney.dsl.PreferPartialTransformer.type]

val FailOnUnused: Type[io.scalaland.chimney.dsl.FailOnUnused.type] =
weakTypeTag[io.scalaland.chimney.dsl.FailOnUnused.type]

val RuntimeDataStore: Type[dsls.TransformerDefinitionCommons.RuntimeDataStore] =
weakTypeTag[dsls.TransformerDefinitionCommons.RuntimeDataStore]

Expand Down Expand Up @@ -249,6 +252,17 @@ private[compiletime] trait ChimneyTypesPlatform extends ChimneyTypes { this: Chi
)
}
}
object IgnoreUnusedField extends IgnoreUnusedFieldModule {
def apply[
FromPath <: runtime.Path: Type,
Tail <: runtime.TransformerOverrides: Type
]: Type[runtime.TransformerOverrides.IgnoreUnusedField[FromPath, Tail]] =
weakTypeTag[runtime.TransformerOverrides.IgnoreUnusedField[FromPath, Tail]]
def unapply[A](A: Type[A]): Option[(?<[runtime.Path], ?<[runtime.TransformerOverrides])] =
A.asCtor[runtime.TransformerOverrides.IgnoreUnusedField[?, ?]].map { A0 =>
(A0.param_<[runtime.Path](0), A0.param_<[runtime.TransformerOverrides](1))
}
}
}

object TransformerFlags extends TransformerFlagsModule {
Expand Down Expand Up @@ -309,6 +323,15 @@ private[compiletime] trait ChimneyTypesPlatform extends ChimneyTypes { this: Chi
A0.param_<[dsls.ImplicitTransformerPreference](0)
}
}
object UnusedFieldPolicy extends UnusedFieldPolicyModule {
def apply[R <: dsls.ActionOnUnused: Type]
: Type[runtime.TransformerFlags.UnusedFieldPolicy[R]] =
weakTypeTag[runtime.TransformerFlags.UnusedFieldPolicy[R]]
def unapply[A](A: Type[A]): Option[?<[dsls.ActionOnUnused]] =
A.asCtor[runtime.TransformerFlags.UnusedFieldPolicy[?]].map { A0 =>
A0.param_<[dsls.ActionOnUnused](0)
}
}
object FieldNameComparison extends FieldNameComparisonModule {
def apply[C <: dsls.TransformedNamesComparison: Type]: Type[runtime.TransformerFlags.FieldNameComparison[C]] =
weakTypeTag[runtime.TransformerFlags.FieldNameComparison[C]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,17 @@ class TransformerDefinitionMacros(val c: whitebox.Context) extends utils.DslMacr
.addOverride(f)
.asInstanceOfExpr[TransformerDefinition[From, To, Constructor[Args, Path.Root, Overrides], Flags]]
}.applyFromBody(f)

def withIgnoreUnusedField[
From: WeakTypeTag,
To: WeakTypeTag,
Overrides <: TransformerOverrides : WeakTypeTag,
Flags <: TransformerFlags : WeakTypeTag
](selectorFrom: Tree): Tree = c.prefix.tree
.asInstanceOfExpr(
new ApplyFieldNameType {
def apply[FromPath <: Path : WeakTypeTag]: c.WeakTypeTag[?] =
weakTypeTag[TransformerDefinition[From, To, IgnoreUnusedField[FromPath, Overrides], Flags]]
}.applyFromSelector(selectorFrom)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,17 @@ class TransformerIntoMacros(val c: whitebox.Context) extends utils.DslMacroUtils
.addOverride(f)
.asInstanceOfExpr[TransformerInto[From, To, Constructor[Args, Path.Root, Overrides], Flags]]
}.applyFromBody(f)

def withIgnoreUnusedField[
From: WeakTypeTag,
To: WeakTypeTag,
Overrides <: TransformerOverrides: WeakTypeTag,
Flags <: TransformerFlags: WeakTypeTag
](selectorFrom: Tree): Tree = c.prefix.tree
.asInstanceOfExpr(
new ApplyFieldNameType {
def apply[FromPath <: Path: WeakTypeTag]: c.WeakTypeTag[?] =
weakTypeTag[TransformerInto[From, To, IgnoreUnusedField[FromPath, Overrides], Flags]]
}.applyFromSelector(selectorFrom)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,20 @@ final class TransformerDefinition[From, To, Overrides <: TransformerOverrides, F
)(using IsFunction.Of[Ctor, To]): TransformerDefinition[From, To, ? <: TransformerOverrides, Flags] =
${ TransformerDefinitionMacros.withConstructorImpl('this, 'f) }

/** Ignore if a source field is not used in the transformation. This can be useful when `.enableUnusedFieldPolicy` is
* enabled.
*
* @param selectorFrom
* the field is that not required to be used in the transformation
* @return
* [[io.scalaland.chimney.dsl.TransformerInto]]
* @since 1.5.0
*/
transparent inline def withIgnoreUnusedField(
inline selectorFrom: From => ?
): TransformerDefinition[From, To, ? <: TransformerOverrides, Flags] =
${ TransformerDefinitionMacros.withIgnoreUnusedFieldImpl('this, 'selectorFrom) }

/** Build Transformer using current configuration.
*
* It runs macro that tries to derive instance of `Transformer[From, To]`. When transformation can't be derived, it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,21 @@ final class TransformerInto[From, To, Overrides <: TransformerOverrides, Flags <
)(using IsFunction.Of[Ctor, To]): TransformerInto[From, To, ? <: TransformerOverrides, Flags] =
${ TransformerIntoMacros.withConstructorImpl('this, 'f) }

/** Ignore if a source field is not used in the transformation. This can be useful when `.enableUnusedFieldPolicy` is
* enabled.
*
* @param selectorFrom
* the field is that not required to be used in the transformation
* @return
* [[io.scalaland.chimney.dsl.TransformerInto]]
*
* @since 1.5.0
*/
transparent inline def withIgnoreUnusedField(
inline selectorFrom: From => ?
): TransformerInto[From, To, ? <: TransformerOverrides, Flags] =
${ TransformerIntoMacros.withIgnoreUnusedFieldImpl('this, 'selectorFrom) }

/** Apply configured transformation in-place.
*
* It runs macro that tries to derive instance of `Transformer[From, To]` and immediately apply it to captured
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ private[compiletime] trait ChimneyTypesPlatform extends ChimneyTypes { this: Chi
val PreferPartialTransformer: Type[io.scalaland.chimney.dsl.PreferPartialTransformer.type] =
quoted.Type.of[io.scalaland.chimney.dsl.PreferPartialTransformer.type]

val FailOnUnused: Type[io.scalaland.chimney.dsl.FailOnUnused.type] =
quoted.Type.of[io.scalaland.chimney.dsl.FailOnUnused.type]

val RuntimeDataStore: Type[dsls.TransformerDefinitionCommons.RuntimeDataStore] =
quoted.Type.of[dsls.TransformerDefinitionCommons.RuntimeDataStore]

Expand Down Expand Up @@ -219,6 +222,19 @@ private[compiletime] trait ChimneyTypesPlatform extends ChimneyTypes { this: Chi
case _ => scala.None
}
}
object IgnoreUnusedField extends IgnoreUnusedFieldModule {
def apply[
FromPath <: runtime.Path: Type,
Tail <: runtime.TransformerOverrides: Type
]: Type[runtime.TransformerOverrides.IgnoreUnusedField[FromPath, Tail]] =
quoted.Type.of[runtime.TransformerOverrides.IgnoreUnusedField[FromPath, Tail]]
def unapply[A](tpe: Type[A]): Option[(?<[runtime.Path], ?<[runtime.TransformerOverrides])] =
tpe match {
case '[runtime.TransformerOverrides.IgnoreUnusedField[fromPath, cfg]] =>
Some((Type[fromPath].as_?<[runtime.Path], Type[cfg].as_?<[runtime.TransformerOverrides]))
case _ => scala.None
}
}
}

object TransformerFlags extends TransformerFlagsModule {
Expand Down Expand Up @@ -286,6 +302,15 @@ private[compiletime] trait ChimneyTypesPlatform extends ChimneyTypes { this: Chi
case _ => scala.None
}
}
object UnusedFieldPolicy extends UnusedFieldPolicyModule {
def apply[R <: dsls.ActionOnUnused: Type]: Type[runtime.TransformerFlags.UnusedFieldPolicy[R]] =
quoted.Type.of[runtime.TransformerFlags.UnusedFieldPolicy[R]]
def unapply[A](tpe: Type[A]): Option[?<[dsls.ActionOnUnused]] = tpe match {
case '[runtime.TransformerFlags.UnusedFieldPolicy[r]] =>
Some(Type[r].as_?<[dsls.ActionOnUnused])
case _ => scala.None
}
}
object FieldNameComparison extends FieldNameComparisonModule {
def apply[C <: dsls.TransformedNamesComparison: Type]: Type[runtime.TransformerFlags.FieldNameComparison[C]] =
quoted.Type.of[runtime.TransformerFlags.FieldNameComparison[C]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,21 @@ object TransformerDefinitionMacros {
.asInstanceOf[TransformerDefinition[From, To, Constructor[args, Path.Root, Overrides], Flags]]
}
}(f)

def withIgnoreUnusedFieldImpl[
From: Type,
To: Type,
Overrides <: TransformerOverrides : Type,
Flags <: TransformerFlags : Type
](
ti: Expr[TransformerDefinition[From, To, Overrides, Flags]],
fromSelector: Expr[From => ?]
)(using Quotes): Expr[TransformerDefinition[From, To, ? <: TransformerOverrides, Flags]] =
DslMacroUtils().applyFieldNameType {
[fromPath <: Path] =>
(_: Type[fromPath]) ?=>
'{
$ti.asInstanceOf[TransformerDefinition[From, To, IgnoreUnusedField[fromPath, Overrides], Flags]]
}
}(fromSelector)
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,21 @@ object TransformerIntoMacros {
.asInstanceOf[TransformerInto[From, To, Constructor[args, Path.Root, Overrides], Flags]]
}
}(f)

def withIgnoreUnusedFieldImpl[
From: Type,
To: Type,
Overrides <: TransformerOverrides: Type,
Flags <: TransformerFlags: Type
](
ti: Expr[TransformerInto[From, To, Overrides, Flags]],
fromSelector: Expr[From => ?]
)(using Quotes): Expr[TransformerInto[From, To, ? <: TransformerOverrides, Flags]] =
DslMacroUtils().applyFieldNameType {
[fromPath <: Path] =>
(_: Type[fromPath]) ?=>
'{
$ti.asInstanceOf[TransformerInto[From, To, IgnoreUnusedField[fromPath, Overrides], Flags]]
}
}(fromSelector)
}
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,5 @@ private[chimney] class DslMacroUtils()(using quotes: Quotes) {
case Right(ctorType) => f(using ctorType.Underlying)
case Left(error) => report.errorAndAbort(error, Position.ofMacroExpansion)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.scalaland.chimney.dsl

/** Action to take when some fields of the source are not used in the target.
*
* @see
* [[https://chimney.readthedocs.io/supported-transformations/#unused-source-fields-policies]] for more details
*
* @since 1.5.0
*/
sealed abstract class ActionOnUnused

/** Fail the derivation if not all fields of the source are not used. Exceptions can be made using
* `ignoreUnusedField` overrides.
*
* @see
* [[https://chimney.readthedocs.io/supported-transformations/#unused-source-fields-policies]] for more details
*
* @since 1.5.0
*/
case object FailOnUnused extends ActionOnUnused
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,27 @@ private[dsl] trait TransformerFlagsDsl[UpdateFlag[_ <: TransformerFlags], Flags
def disableMacrosLogging: UpdateFlag[Disable[MacrosLogging, Flags]] =
disableFlag[MacrosLogging]

/** Enable an action to be executed upon unused fields in the source type.
*
* @param action
* parameter specifying what to do when some fields of the source are not used in the target
* @see
* [[https://chimney.readthedocs.io/supported-transformations/#unused-source-fields-policies]] for more details for more details
*
* @since 1.5.0
*/
def enableUnusedFieldPolicy[R <: ActionOnUnused](@unused action: R): UpdateFlag[Enable[UnusedFieldPolicy[R], Flags]] =
enableFlag[UnusedFieldPolicy[R]]

/** Disable any action registered to be executed upon unused fields in the source type.
*
* @see
* [[https://chimney.readthedocs.io/TODO:???]] for more details for more details
* @since 1.5.0
*/
def disableUnusedFieldPolicy: UpdateFlag[Disable[UnusedFieldPolicy[?], Flags]] =
disableFlag[UnusedFieldPolicy[?]]

private def enableFlag[F <: TransformerFlags.Flag]: UpdateFlag[Enable[F, Flags]] =
this.asInstanceOf[UpdateFlag[Enable[F, Flags]]]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ private[compiletime] trait ChimneyTypes { this: ChimneyDefinitions =>
val PreferTotalTransformer: Type[io.scalaland.chimney.dsl.PreferTotalTransformer.type]
val PreferPartialTransformer: Type[io.scalaland.chimney.dsl.PreferPartialTransformer.type]

val FailOnUnused: Type[io.scalaland.chimney.dsl.FailOnUnused.type]

val RuntimeDataStore: Type[dsls.TransformerDefinitionCommons.RuntimeDataStore]

val ArgumentList: ArgumentListModule
Expand Down Expand Up @@ -151,6 +153,14 @@ private[compiletime] trait ChimneyTypes { this: ChimneyDefinitions =>
runtime.TransformerOverrides,
runtime.TransformerOverrides.RenamedTo
] { this: RenamedTo.type => }

val IgnoreUnusedField: IgnoreUnusedFieldModule
trait IgnoreUnusedFieldModule
extends Type.Ctor2UpperBounded[
runtime.Path,
runtime.TransformerOverrides,
runtime.TransformerOverrides.IgnoreUnusedField
] { this: IgnoreUnusedField.type => }
}

val TransformerFlags: TransformerFlagsModule
Expand Down Expand Up @@ -196,6 +206,13 @@ private[compiletime] trait ChimneyTypes { this: ChimneyDefinitions =>
dsls.ImplicitTransformerPreference,
runtime.TransformerFlags.ImplicitConflictResolution
] { this: ImplicitConflictResolution.type => }
val UnusedFieldPolicy: UnusedFieldPolicyModule
trait UnusedFieldPolicyModule
extends Type.Ctor1UpperBounded[
dsls.ActionOnUnused,
runtime.TransformerFlags.UnusedFieldPolicy
] {
this: UnusedFieldPolicy.type => }
val FieldNameComparison: FieldNameComparisonModule
trait FieldNameComparisonModule
extends Type.Ctor1UpperBounded[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ final case class NotSupportedTransformerDerivation(
)(val fromType: String, val toType: String)
extends TransformerDerivationError

final case class UnusedButRequiredToUseSourceFields(
unusedFields: Set[String]
)(val fromType: String, val toType: String)
extends TransformerDerivationError

object TransformerDerivationError {
def printErrors(errors: Seq[TransformerDerivationError]): String =
errors
Expand Down Expand Up @@ -108,6 +113,8 @@ object TransformerDerivationError {
| Please eliminate total/partial ambiguity from implicit scope or use ${MAGENTA}enableImplicitConflictResolution$RESET/${MAGENTA}withFieldComputed$RESET/${MAGENTA}withFieldComputedPartial$RESET to decide which one should be used.""".stripMargin
case NotSupportedTransformerDerivation(exprPrettyPrint) =>
s" derivation from $exprPrettyPrint: $fromType to $toType is not supported in Chimney!"
case UnusedButRequiredToUseSourceFields(unusedFields) =>
s" field(s) $MAGENTA${unusedFields.mkString(", ")}$RESET of $MAGENTA${fromType}$RESET were required to be used in the transformation but are not used!"
}

def prettyFieldList(fields: Seq[String])(use: String => String): String =
Expand Down
Loading
Loading