1
+ import scala .deriving .Mirror as M
1
2
import scala .deriving .*
2
3
import scala .Tuple .*
3
4
import scala .compiletime .*
4
5
import scala .compiletime .ops .int .S
5
6
6
- trait Migration [From , To ]:
7
+ trait Migration [- From , + To ]:
7
8
def apply (x : From ): To
8
9
9
10
object Migration :
@@ -14,62 +15,56 @@ object Migration:
14
15
given [T ]: Migration [T , T ] with
15
16
override def apply (x : T ): T = x
16
17
17
- private type IndexOf [Elems <: Tuple , X ] <: Int = Elems match {
18
- case (elem *: elems) =>
19
- elem match {
20
- case X => 0
21
- case _ => S [IndexOf [elems, X ]]
22
- }
18
+ type IndexOf [Elems <: Tuple , X ] <: Int = Elems match {
19
+ case (X *: elems) => 0
20
+ case (_ *: elems) => S [IndexOf [elems, X ]]
23
21
case EmptyTuple => Nothing
24
22
}
25
23
26
- private inline def migrate [F ,T ](x : F ): T = summonFrom {
27
- case migration : Migration [F ,T ] => migration(x)
28
- }
24
+ inline def migrateElem [F ,T , ToIdx <: Int ](from : M .ProductOf [F ], to : M .ProductOf [T ])(x : Product ): Any =
29
25
30
- private inline def migrateElem [F ,T , ToIdx <: Int ]
31
- (from : Mirror .ProductOf [F ], to : Mirror .ProductOf [T ])
32
- (x : Product ): Any =
33
26
type Label = Elem [to.MirroredElemLabels , ToIdx ]
34
27
type FromIdx = IndexOf [from.MirroredElemLabels , Label ]
35
28
inline constValueOpt[FromIdx ] match
29
+
36
30
case Some (fromIdx) =>
37
31
type FromType = Elem [from.MirroredElemTypes , FromIdx ]
38
32
type ToType = Elem [to.MirroredElemTypes , ToIdx ]
39
- val elem = x.productElement(fromIdx).asInstanceOf [FromType ]
40
- migrate[FromType , ToType ](elem)
33
+ summonFrom { case _ : Migration [FromType , ToType ] =>
34
+ x.productElement(fromIdx).asInstanceOf [FromType ].migrateTo[ToType ]
35
+ }
36
+
41
37
case None =>
42
- type HasDefault = Elem [to.MirroredElemHasDefaults , ToIdx ] // NOTE when are the annotations checked ?? bug expr is not checked for match types
38
+ type HasDefault = Elem [to.MirroredElemHasDefaults , ToIdx ]
43
39
inline erasedValue[HasDefault ] match
44
40
case _ : true => to.defaultArgument(constValue[ToIdx ])
45
- case _ : false => compiletime.error(" An element has no equivalent in source or default" )
46
- end migrateElem
41
+ case _ : false => compiletime.error(" An element has no equivalent or default" )
47
42
48
- private inline def migrateElems [F ,T , ToIdx <: Int ]
49
- (from : Mirror .ProductOf [F ], to : Mirror .ProductOf [T ])
50
- (x : Product ): Seq [Any ] =
43
+
44
+ inline def migrateElems [F ,T , ToIdx <: Int ](from : M .ProductOf [F ], to : M .ProductOf [T ])(x : Product ): Seq [Any ] =
51
45
inline erasedValue[ToIdx ] match
52
46
case _ : Tuple .Size [to.MirroredElemLabels ] => Seq ()
53
47
case _ => migrateElem[F ,T ,ToIdx ](from, to)(x) +: migrateElems[F ,T ,S [ToIdx ]](from, to)(x)
54
48
55
- private inline def migrateProduct [F ,T ](from : Mirror .ProductOf [F ], to : Mirror .ProductOf [T ])
56
- (x : Product ): T =
57
- val elems = migrateElems[F ,T , 0 ](from, to)(x)
49
+ inline def migrateProduct [F ,T ](from : M .ProductOf [F ], to : M .ProductOf [T ])
50
+ (x : Product ): T =
51
+ val elems = migrateElems[F , T , 0 ](from, to)(x)
58
52
to.fromProduct(new Product :
59
- override def canEqual (that : Any ): Boolean = false
60
- override def productArity : Int = x.productArity
61
- override def productElement (n : Int ): Any = elems(n)
53
+ def canEqual (that : Any ): Boolean = false
54
+ def productArity : Int = elems.length
55
+ def productElement (n : Int ): Any = elems(n)
62
56
)
63
57
64
- implicit inline def migration [F ,T ](using from : Mirror .Of [F ], to : Mirror .Of [T ]): Migration [F ,T ] =
65
- new Migration [ F , T ] :
66
- override def apply ( x : F ) : T = inline from match
67
- case fromP : Mirror .ProductOf [F ] => inline to match
68
- case _ : Mirror .SumOf [T ] => compiletime.error(" Can not migrate a product to a sum " )
69
- case toP : Mirror . ProductOf [ T ] => migrateProduct[ F , T ](fromP, toP)(x. asInstanceOf [ Product ] )
58
+ inline def migration [F ,T ](using from : M .Of [F ], to : M .Of [T ]): Migration [F ,T ] = ( x : F ) =>
59
+ inline from match
60
+ case fromP : M . ProductOf [ F ] => inline to match
61
+ case toP : M .ProductOf [T ] => migrateProduct[ F , T ](fromP, toP)(x. asInstanceOf [ Product ])
62
+ case _ : M .SumOf [T ] => compiletime.error(" Cannot migrate sums " )
63
+ case _ : M . SumOf [ F ] => compiletime.error( " Cannot migrate sums " )
70
64
71
65
end Migration
72
66
67
+
73
68
import Migration .*
74
69
object Test extends App :
75
70
@@ -98,6 +93,7 @@ object Test extends App:
98
93
given Migration [E1 , E2 ] = migration
99
94
assert(E1 (D1 (1 ), D1 (2 )).migrateTo[E2 ] == E2 (D2 (true , 2 ), " hi" , D2 (true , 1 )))
100
95
96
+ // should only use default when needed
101
97
case class F1 (x : Int )
102
98
case class F2 (x : Int = 3 )
103
99
given Migration [F1 , F2 ] = migration
0 commit comments