module Data.Migration.Internal where
import Data.Generics.Product
import Data.Kind
import Lens.Micro hiding (to)
import GHC.Generics
import GHC.TypeLits
type family RepToTree (a :: Type -> Type) :: [(Symbol, Type)] where
RepToTree (f :*: g) = RepToTree f ++ RepToTree g
RepToTree (M1 S ('MetaSel ('Just name) _1 _2 _3) (K1 _4 t)) = '[ '(name, t) ]
RepToTree (M1 _1 _2 f) = RepToTree f
type family (++) (xs :: [k]) (ys :: [k]) :: [k] where
(++) '[] ys = ys
(++) (x ': xs) ys = x ': (xs ++ ys)
type family Sort (xs :: [(Symbol, k)]) where
Sort '[] = '[]
Sort (x ': xs) = Insert x (Sort xs)
type family Insert (x :: (Symbol, k)) (xs :: [(Symbol, k)]) where
Insert x '[] = x ': '[]
Insert '(x, t) ('(y, t') ': ys) = Insert' (CmpSymbol x y) '(x, t) '(y, t') ys
type family Insert' (b :: Ordering) (x :: (Symbol, k)) (y :: (Symbol, k)) (ys :: [(Symbol, k)]) where
Insert' 'LT x y ys = x ': (y ': ys)
Insert' _ x y ys = y ': Insert x ys
data DiffResult
= NoChange Symbol Type
| Addition Symbol Type
| Change Symbol Type Type
type family FieldDiff (a :: [(Symbol, Type)])
(b :: [(Symbol, Type)]) :: [DiffResult] where
FieldDiff xs '[] = '[]
FieldDiff '[] ('(y, v) ': ys) = 'Addition y v ': FieldDiff '[] ys
FieldDiff ('(x, t) ': xs) ('(x, t) ': ys) = 'NoChange x t ': FieldDiff xs ys
FieldDiff ('(x, u) ': xs) ('(x, v) ': ys) = 'Change x u v ': FieldDiff xs ys
FieldDiff ('(x, u) ': xs) ('(y, v) ': ys) = FieldDiffImpl (CmpSymbol x y) '(x, u) '(y, v) xs ys
type family FieldDiffImpl (b :: Ordering)
(x :: (Symbol, Type))
(y :: (Symbol, Type))
(xs :: [(Symbol, Type)])
(ys :: [(Symbol, Type)]) :: [DiffResult] where
FieldDiffImpl 'LT _ y xs ys = FieldDiff xs (y ': ys)
FieldDiffImpl 'GT x '(y, v) xs ys = 'Addition y v ': FieldDiff (x ': xs) ys
copyField
:: forall name t from to
. ( HasField' name to t
, HasField' name from t
)
=> from
-> to
-> to
copyField f t =
t & field' @name .~ f ^. field' @name
class GTransform (ts :: [DiffResult]) (src :: Type) (dst :: Type) where
type Function ts src dst :: Type
gTransform :: dst -> src -> Function ts src dst
instance (Generic dst, GUndefinedFields (Rep dst)) => GTransform '[] src dst where
type Function '[] src dst = dst
gTransform dst _ = dst
instance ( GTransform ts src dst
, HasField' name src t
, HasField' name dst t
) => GTransform ('NoChange name t ': ts) src dst where
type Function ('NoChange name t ': ts) src dst = Function ts src dst
gTransform dst src = gTransform @ts (copyField @name src dst) src
instance ( GTransform ts src dst
, HasField' name dst t
) => GTransform ('Addition name t ': ts) src dst where
type Function ('Addition name t ': ts) src dst = (src -> t) -> Function ts src dst
gTransform dst src mk_t = gTransform @ts (dst & field' @name .~ mk_t src) src
instance ( GTransform ts src dst
, HasField' name src ti
, HasField' name dst to
) => GTransform ('Change name ti to ': ts) src dst where
type Function ('Change name ti to ': ts) src dst = (src -> ti -> to) -> Function ts src dst
gTransform dst src mk_to = gTransform @ts (dst & field' @name .~ mk_to src (src ^. field' @name)) src
class GUndefinedFields (o :: * -> *) where
gUndefinedFields :: o x
instance GUndefinedFields o => GUndefinedFields (M1 _3 _4 o) where
gUndefinedFields = M1 $ gUndefinedFields
instance (GUndefinedFields o1, GUndefinedFields o2) => GUndefinedFields (o1 :*: o2) where
gUndefinedFields = gUndefinedFields :*: gUndefinedFields
instance GUndefinedFields (K1 _1 t) where
gUndefinedFields = K1 undefined
undefinedFields :: (Generic t, GUndefinedFields (Rep t)) => t
undefinedFields = to gUndefinedFields