diff --git a/chimney/src/main/scala/io/scalaland/chimney/internal/compiletime/derivation/transformer/Configurations.scala b/chimney/src/main/scala/io/scalaland/chimney/internal/compiletime/derivation/transformer/Configurations.scala index daed97daf..1f6d0840e 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/internal/compiletime/derivation/transformer/Configurations.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/internal/compiletime/derivation/transformer/Configurations.scala @@ -591,7 +591,7 @@ private[compiletime] trait Configurations { this: Derivation => // checking if there might be some relevant overrides for current/nested values case _: TransformerOverride.ForField | _: TransformerOverride.ForSubtype => val newSidedPath = sidedPath.drop(fromPath, toPath).view.filterNot(_.path == Path.Root) - newSidedPath.map(_ -> runtimeOverride) + newSidedPath.map(_ -> runtimeOverride).filterNot(pathCannotBeUsedButBlocksRuleForEmptyOverrides) // Fallbacks are always matched at "_" Path, and dropped _manually_ only when going inward, // because we have to update their inner value case f: TransformerOverride.ForFallback => @@ -607,6 +607,12 @@ private[compiletime] trait Configurations { this: Derivation => preventImplicitSummoningForTypes = None ) + // I haven't found a more "principled" way to achieve this, that wouldn't break half the tests + private lazy val pathCannotBeUsedButBlocksRuleForEmptyOverrides: ((SidedPath, TransformerOverride)) => Boolean = { + case (TargetPath(Path.AtSubtype(_, Path.Root)), _: TransformerOverride.Renamed) => true + case _ => false + } + override def toString: String = { val runtimeOverridesString = runtimeOverrides.map { case (path, runtimeOverride) => s"$path -> $runtimeOverride" }.mkString(", ") diff --git a/chimney/src/test/scala/io/scalaland/chimney/IssuesSpec.scala b/chimney/src/test/scala/io/scalaland/chimney/IssuesSpec.scala index 811e5b5d0..f293834b4 100644 --- a/chimney/src/test/scala/io/scalaland/chimney/IssuesSpec.scala +++ b/chimney/src/test/scala/io/scalaland/chimney/IssuesSpec.scala @@ -827,4 +827,19 @@ class IssuesSpec extends ChimneySpec { Foo(1, "value").transformIntoPartial[Bar].asOption.get ==> Bar(20, "value") Foo(1, "value").patchUsing(Baz(30)) ==> Foo(30, "patched") } + + test("fix issue #683") { + import Issue683.* + + (Proto.Foo(10): Proto) + .intoPartial[Domain] + .withSealedSubtypeRenamed[Proto.Foo, Domain.Foo1] + .transform + .asOption ==> Some(Domain.Foo1(10)) + (Proto.Empty: Proto) + .intoPartial[Domain] + .withSealedSubtypeRenamed[Proto.Foo, Domain.Foo1] + .transform + .asOption ==> None + } } diff --git a/chimney/src/test/scala/io/scalaland/chimney/fixtures/Issues.scala b/chimney/src/test/scala/io/scalaland/chimney/fixtures/Issues.scala index be140dd94..0415fb712 100644 --- a/chimney/src/test/scala/io/scalaland/chimney/fixtures/Issues.scala +++ b/chimney/src/test/scala/io/scalaland/chimney/fixtures/Issues.scala @@ -228,3 +228,19 @@ object Issue479 { case class Impl(value: String) extends Target } } + +object Issue683 { + sealed trait Proto extends Product with Serializable + object Proto { + case class Foo(a: Int) extends Proto + case object Empty extends Proto + } + + sealed trait Domain extends Product with Serializable + object Domain { + case class Foo1(a: Int) extends Domain + } + + implicit def handleEmpty[To]: PartialTransformer[Proto.Empty.type, To] = + (_: Proto.Empty.type, _: Boolean) => partial.Result.fromEmpty[To] +}