sqel-0.0.1.0: lib/Sqel/ReifyCodec.hs
module Sqel.ReifyCodec where
import Data.Functor.Invariant (Invariant, invmap)
import Generics.SOP (I (I), NP (Nil, (:*)))
import qualified Hasql.Encoders as Encoders
import Sqel.Codec (
ColumnDecoder (columnDecoder, columnDecoderNullable),
ColumnEncoder (columnEncoder, columnEncoderIgnore, columnEncoderNullable),
PrimColumn (primDecoder, primEncoder),
fullPrimCodec,
ignoreDecoder,
)
import Sqel.Codec.PrimDecoder (ArrayDecoder (arrayDecoder), enumDecoder, readDecoder)
import Sqel.Codec.PrimEncoder (arrayEncoder)
import Sqel.Codec.Product (ProdCodec (prodCodec))
import Sqel.Codec.Sum (ConCodec (conCodec), SumCodec (sumCodec), ignoreEncoder)
import qualified Sqel.Data.Codec as Codec
import Sqel.Data.Codec (Codec (Codec), Decoder (Decoder), Encoder (Encoder), FullCodec, ValueCodec)
import Sqel.Data.Dd (
Comp (Prod, Sum),
CompInc,
ConCol,
Dd (Dd),
DdK (DdK),
DdStruct (DdComp, DdPrim),
ProdType (Con, Reg),
Struct (Comp, Prim),
)
import Sqel.Data.Mods (
ArrayColumn (ArrayColumn),
EnumColumn (EnumColumn),
Ignore (Ignore),
Mods (Mods),
Newtype (Newtype),
Nullable (Nullable),
ReadShowColumn (ReadShowColumn),
)
import Sqel.Mods (PrimCodec (PrimCodec), PrimValueCodec, PrimValueEncoder)
import Sqel.SOP.Enum (EnumTable)
type CompCodec :: Comp -> CompInc -> Type -> (Type -> Type) -> [Type] -> Constraint
class CompCodec c i a b as where
compCodec :: NP b as -> b a
instance (
ProdCodec b a as
) => CompCodec ('Prod 'Reg) i a b as where
compCodec = prodCodec
instance (
ConCodec b as
) => CompCodec ('Prod ('Con as)) i (ConCol name record fields as) b as where
compCodec = conCodec
instance (
SumCodec b a as
) => CompCodec 'Sum i a b as where
compCodec = sumCodec
class DefaultPrimCodec b a where
defaultPrimCodec :: b a
instance (
PrimColumn a
) => DefaultPrimCodec FullCodec a where
defaultPrimCodec =
Codec {
encoder = Encoder (columnEncoder encoder) (columnEncoderIgnore encoder),
decoder = Decoder (columnDecoder decoder) (void ignoreDecoder)
}
where
encoder = primEncoder
decoder = primDecoder
instance (
PrimColumn a
) => DefaultPrimCodec ValueCodec a where
defaultPrimCodec =
Codec {
encoder = primEncoder,
decoder = primDecoder
}
instance (
PrimColumn a
) => DefaultPrimCodec Encoder a where
defaultPrimCodec =
Encoder (columnEncoder encoder) (columnEncoderIgnore encoder)
where
encoder = primEncoder
instance (
PrimColumn a
) => DefaultPrimCodec Encoders.Value a where
defaultPrimCodec = primEncoder
type DefaultCompCodec :: Comp -> CompInc -> (Type -> Type) -> Type -> [Type] -> Constraint
class DefaultCompCodec c i b a as where
defaultCompCodec :: NP b as -> b a
instance (
CompCodec c i a FullCodec as
) => DefaultCompCodec c i FullCodec a as where
defaultCompCodec = compCodec @c @i
instance (
CompCodec c i a Encoder as
) => DefaultCompCodec c i Encoder a as where
defaultCompCodec = compCodec @c @i
class ReifyPrimCodec b ps a where
reifyPrimCodec :: NP I ps -> b a
instance {-# overlappable #-} (
ReifyPrimCodec b ps a
) => ReifyPrimCodec b (p : ps) a where
reifyPrimCodec (_ :* ps) =
reifyPrimCodec ps
instance ReifyPrimCodec ValueCodec (PrimValueCodec a : ps) a where
reifyPrimCodec (I (PrimCodec c) :* _) = c
instance ReifyPrimCodec FullCodec (PrimValueCodec a : ps) a where
reifyPrimCodec (I (PrimCodec (Codec encoder decoder)) :* _) =
Codec {
encoder = Encoder (columnEncoder encoder) (columnEncoderIgnore encoder),
decoder = Decoder (columnDecoder decoder) (void ignoreDecoder)
}
instance (
ReifyPrimCodec ValueCodec ps a
) => ReifyPrimCodec FullCodec (Nullable : ps) (Maybe a) where
reifyPrimCodec (I Nullable :* ps) =
Codec {
encoder = Encoder (columnEncoderNullable encoder) (ignoreEncoder encoder),
decoder = Decoder (columnDecoderNullable decoder) (void ignoreDecoder)
}
where
Codec encoder decoder = reifyPrimCodec @ValueCodec ps
instance ReifyPrimCodec Encoders.Value (PrimValueEncoder a : ps) a where
reifyPrimCodec (I (PrimCodec e) :* _) = e
instance ReifyPrimCodec Encoder (PrimValueEncoder a : ps) a where
reifyPrimCodec (I (PrimCodec encoder) :* _) =
Encoder (columnEncoder encoder) (columnEncoderIgnore encoder)
-- TODO this could also produce NullableOrNot
instance (
ReifyPrimCodec Encoders.Value ps a
) => ReifyPrimCodec Encoder (Nullable : ps) (Maybe a) where
reifyPrimCodec (I Nullable :* ps) =
Encoder (columnEncoderNullable encoder) (ignoreEncoder encoder)
where
encoder = reifyPrimCodec @Encoders.Value ps
instance (
Show a,
EnumTable a
) => ReifyPrimCodec FullCodec (EnumColumn : ps) a where
reifyPrimCodec (I EnumColumn :* _) =
fullPrimCodec (Encoders.enum show) enumDecoder
instance (
Show a,
EnumTable a
) => ReifyPrimCodec ValueCodec (EnumColumn : ps) a where
reifyPrimCodec (I EnumColumn :* _) =
Codec (Encoders.enum show) enumDecoder
instance (
Show a,
Read a
) => ReifyPrimCodec FullCodec (ReadShowColumn : ps) a where
reifyPrimCodec (I ReadShowColumn :* _) =
fullPrimCodec (Encoders.enum show) readDecoder
instance (
Show a,
Read a
) => ReifyPrimCodec ValueCodec (ReadShowColumn : ps) a where
reifyPrimCodec (I ReadShowColumn :* _) =
Codec (Encoders.enum show) readDecoder
instance (
ReifyPrimCodec ValueCodec ps a,
Foldable f,
ArrayDecoder f a
) => ReifyPrimCodec ValueCodec (ArrayColumn f : ps) (f a) where
reifyPrimCodec (I ArrayColumn :* ps) =
Codec (arrayEncoder encoder) (arrayDecoder decoder)
where
Codec encoder decoder = reifyPrimCodec @ValueCodec ps
instance (
ReifyPrimCodec ValueCodec ps a,
Foldable f,
ArrayDecoder f a
) => ReifyPrimCodec FullCodec (ArrayColumn f : ps) (f a) where
reifyPrimCodec (I ArrayColumn :* ps) =
fullPrimCodec (arrayEncoder encoder) (arrayDecoder decoder)
where
Codec encoder decoder = reifyPrimCodec @ValueCodec ps
instance {-# overlappable #-} (
ReifyPrimCodec c mods w,
Invariant c
) => ReifyPrimCodec c (Newtype a w : mods) a where
reifyPrimCodec (I (Newtype unwrap wrap) :* mods) =
invmap wrap unwrap (reifyPrimCodec mods)
instance (
ReifyPrimCodec Encoders.Value mods w
) => ReifyPrimCodec Encoders.Value (Newtype a w : mods) a where
reifyPrimCodec (I (Newtype unwrap _) :* mods) =
unwrap >$< (reifyPrimCodec mods)
instance ReifyPrimCodec FullCodec (Ignore : ps) a where
reifyPrimCodec (I Ignore :* _) =
Codec (Encoder mempty mempty) (Decoder (fail err) (fail err))
where
err = "ignored column was used"
instance ReifyPrimCodec Encoder (Ignore : ps) a where
reifyPrimCodec (I Ignore :* _) =
Encoder mempty mempty
instance (
DefaultPrimCodec b a
) => ReifyPrimCodec b '[] a where
reifyPrimCodec _ = defaultPrimCodec
class ReifyCompCodec b c i ps as a where
reifyCompCodec :: NP I ps -> NP b as -> b a
instance (
DefaultCompCodec c i b a as
) => ReifyCompCodec b c i ps as a where
reifyCompCodec _ sub =
defaultCompCodec @c @i sub
type ReifyCodec :: (Type -> Type) -> DdK -> Type -> Constraint
class ReifyCodec b s a | s -> a where
reifyCodec :: Dd s -> b a
instance (
ReifyPrimCodec b ps a
) => ReifyCodec b ('DdK sel ps a 'Prim) a where
reifyCodec (Dd _ (Mods ps) DdPrim) =
reifyPrimCodec @b ps
instance (
ReifyCodecComp b sub as,
ReifyCompCodec b c i ps as a
) => ReifyCodec b ('DdK sel ps a ('Comp tsel c i sub)) a where
reifyCodec (Dd _ (Mods ps) (DdComp _ _ _ sub)) =
reifyCompCodec @b @c @i @ps @as ps (reifyCodecComp @b @sub sub)
-- TODO this is probably only necessary because of a bug in GHC that's fixed in master
type ReifyCodecComp :: (Type -> Type) -> [DdK] -> [Type] -> Constraint
class ReifyCodecComp b s as | s -> as where
reifyCodecComp :: NP Dd s -> NP b as
instance ReifyCodecComp b '[] '[] where
reifyCodecComp Nil = Nil
instance (
ReifyCodec b s a,
ReifyCodecComp b ss as
) => ReifyCodecComp b (s : ss) (a : as) where
reifyCodecComp (d :* ds) = reifyCodec d :* reifyCodecComp ds