Skip to content

Commit a62d0ac

Browse files
committed
fix issue 26
1 parent 47074d3 commit a62d0ac

File tree

6 files changed

+54
-41
lines changed

6 files changed

+54
-41
lines changed

build.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ lazy val ducktape =
3636
project
3737
.in(file("ducktape"))
3838
.settings(
39-
scalacOptions ++= List("-Xcheck-macros", "-no-indent", "-old-syntax", "-Xfatal-warnings", "-deprecation", "-explain"),
39+
scalacOptions ++= List("-Xcheck-macros", "-no-indent", "-old-syntax", "-Xfatal-warnings", "-deprecation"),
4040
libraryDependencies += "org.scalameta" %% "munit" % "1.0.0-M7" % Test,
41-
mimaPreviousArtifacts := Set("io.github.arainko" %% "ducktape" % "0.1.0")
41+
mimaPreviousArtifacts := Set("io.github.arainko" %% "ducktape" % "0.1.0", "io.github.arainko" %% "ducktape" % "0.1.1")
4242
)
4343

4444
lazy val docs =

docs/readme.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final case class PersonButMoreFields(firstName: String, lastName: String, age: I
2323

2424
val personWithMoreFields = PersonButMoreFields("John", "Doe", 30, "SOCIAL-NUM-12345")
2525

26-
val transformed = personWithMoreFields.transformInto[Person]
26+
val transformed = personWithMoreFields.to[Person]
2727

2828
```
2929

@@ -34,7 +34,7 @@ If these requirements are not met, a compiletime error is issued:
3434
```scala mdoc:fail
3535
val person = Person("Jerry", "Smith", 20)
3636

37-
person.transformInto[PersonButMoreFields]
37+
person.to[PersonButMoreFields]
3838

3939
```
4040

@@ -49,14 +49,14 @@ enum Size:
4949
enum ExtraSize:
5050
case ExtraSmall, Small, Medium, Large, ExtraLarge
5151

52-
val transformed = Size.Small.transformInto[ExtraSize]
52+
val transformed = Size.Small.to[ExtraSize]
5353
// transformed: ExtraSize = Small
5454
```
5555

5656
We can't go to a coproduct that doesn't contain all of our cases (name wise):
5757

5858
```scala
59-
val size = ExtraSize.Small.transformInto[Size]
59+
val size = ExtraSize.Small.to[Size]
6060
// error:
6161
// No child named 'ExtraSmall' in Size
6262
```
@@ -234,9 +234,9 @@ final case class WrappedString(value: String) extends AnyVal
234234

235235
val wrapped = WrappedString("I am a String")
236236

237-
val unwrapped = wrapped.transformInto[String]
237+
val unwrapped = wrapped.to[String]
238238

239-
val wrappedAgain = unwrapped.transformInto[WrappedString]
239+
val wrappedAgain = unwrapped.to[WrappedString]
240240
```
241241

242242
#### 8. Defining custom `Transformers`
@@ -301,16 +301,16 @@ case class EvenMoreInside2(str: String, int: Int)
301301

302302
val person = Person(23, Some("str"), Inside("insideStr", 24, EvenMoreInside("evenMoreInsideStr", 25)), Vector.empty)
303303
```
304-
#### Generated code - expansion of `.transformInto`
305-
Calling the `.transformInto` method
304+
#### Generated code - expansion of `.to`
305+
Calling the `.to` method
306306
```scala mdoc:silent
307-
person.transformInto[Person2]
307+
person.to[Person2]
308308
```
309309
expands to:
310310
```scala mdoc:passthrough
311311
import io.github.arainko.ducktape.docs.*
312312

313-
Docs.printCode(person.transformInto[Person2])
313+
Docs.printCode(person.to[Person2])
314314
```
315315

316316
#### Generated code - expansion of `.into`

ducktape/src/main/scala/io/github/arainko/ducktape/internal/macros/ProductTransformerMacros.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,19 +157,23 @@ private[ducktape] final class ProductTransformerMacros(using val quotes: Quotes)
157157

158158
private def resolveTransformation[Source: Type](sourceValue: Expr[Source], source: Field, destination: Field)(using Quotes) =
159159
source.transformerTo(destination) match {
160+
// even though this is taken care of in LiftTransformation.liftTransformation
161+
// we need to do this here due to a compiler bug where multiple matches on a
162+
// Transformer[A, B >: A] the B type get replaced with `B | dest` but only if
163+
// you refer to a case class defined in an object by NOT its full path (it works if you refer to it as the full path)
164+
// workaround for issue: https://github.com/arainko/ducktape/issues/26 until this gets fixed in dotty.
165+
case '{
166+
type a
167+
$transformer: Transformer.Identity[`a`, `a`]
168+
} =>
169+
accessField(sourceValue, source.name)
160170
case '{ $transformer: Transformer[source, dest] } =>
161171
val field = accessField(sourceValue, source.name).asExprOf[source]
162-
// println()
163-
// println(Type.show[dest])
164-
// println(field.asTerm.tpe.widen.show)
165-
// '{ $transformer.transform($field) }.asTerm
166172
LiftTransformation.liftTransformation(transformer, field).asTerm
167173
}
168174

169175
private def accessField(value: Expr[Any], fieldName: String)(using Quotes) = Select.unique(value.asTerm, fieldName)
170176

171-
// Select.unique()
172-
173177
private def constructor(tpe: TypeRepr)(using Quotes): Term = {
174178
val (repr, constructor, tpeArgs) = tpe match {
175179
case AppliedType(repr, reprArguments) => (repr, repr.typeSymbol.primaryConstructor, reprArguments)

ducktape/src/main/scala/io/github/arainko/ducktape/syntax.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import scala.deriving.Mirror
1010
extension [Source](value: Source) {
1111
def into[Dest]: AppliedBuilder[Source, Dest] = AppliedBuilder(value)
1212

13-
inline def transformInto[Dest](using inline transformer: Transformer[Source, Dest]) =
14-
${ LiftTransformation.liftTransformation('transformer, 'value) }
13+
// TODO: Introduce in ducktape 0.2 as a replacement for `.to`, this will break binary compat
14+
// inline def transformInto[Dest](using inline transformer: Transformer[Source, Dest]) =
15+
// ${ LiftTransformation.liftTransformation('transformer, 'value) }
1516

16-
// @deprecated(message = "Use '.transformInto' instead, it includes some additional optimizations", since = "0.1.2")
1717
def to[Dest](using Transformer[Source, Dest]): Dest = Transformer[Source, Dest].transform(value)
1818

1919
transparent inline def intoVia[Func](inline function: Func)(using Mirror.ProductOf[Source], FunctionMirror[Func]) =

ducktape/src/test/scala/io/github/arainko/ducktape/derivation/DerivedTransformerSuite.scala

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class DerivedTransformerSuite extends DucktapeSuite {
6161

6262
val actualComplex =
6363
List(
64-
expectedPrimitive.transformInto[ComplexPerson],
64+
expectedPrimitive.to[ComplexPerson],
6565
expectedPrimitive.into[ComplexPerson].transform(),
6666
expectedPrimitive.via(ComplexPerson.apply),
6767
expectedPrimitive.intoVia(ComplexPerson.apply).transform(),
@@ -71,7 +71,7 @@ class DerivedTransformerSuite extends DucktapeSuite {
7171

7272
val actualPrimitive =
7373
List(
74-
expectedComplex.transformInto[PrimitivePerson],
74+
expectedComplex.to[PrimitivePerson],
7575
expectedComplex.into[PrimitivePerson].transform(),
7676
expectedComplex.via(PrimitivePerson.apply),
7777
expectedComplex.intoVia(PrimitivePerson.apply).transform(),
@@ -105,7 +105,7 @@ class DerivedTransformerSuite extends DucktapeSuite {
105105

106106
val actualComplex =
107107
List(
108-
primitive.transformInto[ComplexPerson],
108+
primitive.to[ComplexPerson],
109109
primitive.into[ComplexPerson].transform(),
110110
primitive.via(ComplexPerson.apply),
111111
primitive.intoVia(ComplexPerson.apply).transform(),
@@ -126,12 +126,12 @@ class DerivedTransformerSuite extends DucktapeSuite {
126126
val expectedFromEnum2Mapping = expectedFromEnum1Mapping.map(_.swap)
127127

128128
Enum1.values.foreach { value =>
129-
val actual = value.transformInto[Enum2]
129+
val actual = value.to[Enum2]
130130
assertEquals(expectedFromEnum1Mapping(value), actual)
131131
}
132132

133133
Enum2.values.foreach { value =>
134-
val actual = value.transformInto[Enum1]
134+
val actual = value.to[Enum1]
135135
assertEquals(expectedFromEnum2Mapping(value), actual)
136136
}
137137
}
@@ -144,7 +144,7 @@ class DerivedTransformerSuite extends DucktapeSuite {
144144
val expected = LessFields(1, 2, 3)
145145
val actual =
146146
List(
147-
more.transformInto[LessFields],
147+
more.to[LessFields],
148148
more.into[LessFields].transform(),
149149
more.via(LessFields.apply),
150150
more.intoVia(LessFields.apply).transform(),
@@ -170,7 +170,7 @@ class DerivedTransformerSuite extends DucktapeSuite {
170170

171171
val actual =
172172
List(
173-
person.transformInto[Person2],
173+
person.to[Person2],
174174
person.into[Person2].transform(),
175175
person.via(Person2.apply),
176176
person.intoVia(Person2.apply).transform(),
@@ -185,8 +185,8 @@ class DerivedTransformerSuite extends DucktapeSuite {
185185
val wrappedString = Wrapped("asd")
186186
val unwrapped = "asd"
187187

188-
assertEquals(wrappedString.transformInto[String], unwrapped)
189-
assertEquals(unwrapped.transformInto[Wrapped[String]], wrappedString)
188+
assertEquals(wrappedString.to[String], unwrapped)
189+
assertEquals(unwrapped.to[Wrapped[String]], wrappedString)
190190
}
191191

192192
test("products with AnyVal fields with type params roundrip to their primitives") {
@@ -198,7 +198,7 @@ class DerivedTransformerSuite extends DucktapeSuite {
198198

199199
val actualUnwrapped =
200200
List(
201-
person.transformInto[UnwrappedPerson[Long]],
201+
person.to[UnwrappedPerson[Long]],
202202
person.into[UnwrappedPerson[Long]].transform(),
203203
person.via(UnwrappedPerson.apply[Long]),
204204
person.intoVia(UnwrappedPerson.apply[Long]).transform(),
@@ -208,7 +208,7 @@ class DerivedTransformerSuite extends DucktapeSuite {
208208

209209
val actualPerson =
210210
List(
211-
unwrapped.transformInto[Person[Long]],
211+
unwrapped.to[Person[Long]],
212212
unwrapped.into[Person[Long]].transform(),
213213
unwrapped.via(Person.apply[Long]),
214214
unwrapped.intoVia(Person.apply[Long]).transform(),
@@ -228,7 +228,7 @@ class DerivedTransformerSuite extends DucktapeSuite {
228228
229229
val less = LessFields(1, 2, 3)
230230
231-
val derived = less.transformInto[MoreFields]
231+
val derived = less.to[MoreFields]
232232
"""
233233
}("No field named 'field4' found in LessFields")
234234
}
@@ -242,6 +242,6 @@ class DerivedTransformerSuite extends DucktapeSuite {
242242
}
243243

244244
test("derivation fails when going from a sum with more cases to a sum with less cases") {
245-
assertFailsToCompileWith("MoreCases.Case3.transformInto[LessCases]")("No child named 'Case4' found in LessCases")
245+
assertFailsToCompileWith("MoreCases.Case3.to[LessCases]")("No child named 'Case4' found in LessCases")
246246
}
247247
}
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
package io.github.arainko.ducktape.issues
22

33
import io.github.arainko.ducktape.*
4-
import io.github.arainko.ducktape.DucktapeSuite
54

65
case class A(anotherCaseClass: A.AnotherCaseClass)
76

87
object A {
98
case class AnotherCaseClass(name: String)
10-
case class B(anotherCaseClass: A.AnotherCaseClass)
11-
}
129

13-
object SomeOtherObject {
10+
// note how AnotherCaseClass is not referred to as A.AnotherCaseClass
11+
case class B(anotherCaseClass: AnotherCaseClass)
1412
}
1513

14+
// https://github.com/arainko/ducktape/issues/26
1615
class Issue26Spec extends DucktapeSuite {
17-
// import A.*
16+
test("derive a correct transformer no matter how you refer to A.AnotherCaseClass inside of `A.B`") {
17+
val expected = A.B(A.AnotherCaseClass("test"))
1818

19-
test("repro") {
2019
val a = A(A.AnotherCaseClass("test"))
21-
val b = a.to[A.B]
20+
val actual =
21+
List(
22+
a.to[A.B],
23+
a.into[A.B].transform(),
24+
a.via(A.B.apply),
25+
a.intoVia(A.B.apply).transform(),
26+
Transformer.define[A, A.B].build().transform(a),
27+
Transformer.defineVia[A](A.B.apply).build().transform(a)
28+
)
29+
30+
actual.foreach(actual => assertEquals(actual, expected))
2231
}
2332

2433
}

0 commit comments

Comments
 (0)