versioning-0.3.0.0: src/Versioning/Internal/Decoding.hs
-- | Encoding-agnosting deserialization utilities.
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Versioning.Internal.Decoding
( -- * Types
Applied
, Apply
, ApplyM
, DecodableTo
, DecodableToFrom
, DecodeAnyVersion
, Decoder (..)
, WithAnyVersion
, WithAnyVersionFrom
-- * Decoding and upgrading
, decodeAnyVersion
, decodeAnyVersionFrom
-- * Decoding and applying an action
, withAnyVersion
, withAnyVersionM
, withAnyVersionFromM
, withAnyVersionFrom
)
where
import Data.Functor.Alt (Alt (..))
import Data.Functor.Identity (Identity (..))
import Data.Kind (Constraint, Type)
import Versioning.Base
import Versioning.Internal.Equality (type (==))
import Versioning.Upgrade
-- | The result type of the action that has been applied to the decoded object
-- with 'withAnyVersion' or 'withAnyVersionM'.
type family Applied (c :: Type -> Constraint) (a :: V -> Type) :: Type
-- | The pure function to apply to the decoded object with 'withAnyVersion'
type Apply a c = forall v. c (a v) => a v -> Applied c a
-- | The action to apply to the decoded object with 'withAnyVersionM'
type ApplyM m a c = forall v. c (a v) => a v -> m (Applied c a)
-- | The function that will perform the actual decoding
newtype Decoder dec enc t a = Decoder (forall v. dec (a v) => enc -> t (a v))
-- | Handy constraint synonym to be used with 'decodeAnyVersion'
type DecodableTo dec v a = DecodableToFrom V0 dec v a
type DecodableToFrom from dec v a = DecodeAnyVersionFrom from v v a dec
decodeAnyVersion
:: forall v a dec enc t
. (Alt t, Applicative t, DecodableTo dec v a)
=> Decoder dec enc t a
-> enc
-> t (a v)
decodeAnyVersion = decodeAnyVersionFrom @V0
-- | Decode by trying all the versions decrementally
-- and upgrade the decoded object to the newest version.
decodeAnyVersionFrom
:: forall from v a dec enc t
. (Alt t, Applicative t, DecodableToFrom from dec v a)
=> Decoder dec enc t a
-> enc
-> t (a v)
decodeAnyVersionFrom = decodeAnyVersion' @(from == v) @from @v @v
-- | Decode by trying all the versions decrementally
-- and apply an action to the decoded object at its original version.
withAnyVersionM
:: forall v c a dec enc m t
. (WithAnyVersion v a c dec, Alt t, Applicative t, Traversable t, Applicative m, c (a v))
=> Decoder dec enc t a
-> ApplyM m a c
-> enc
-> m (t (Applied c a))
withAnyVersionM = withAnyVersionFromM @V0 @v @c
-- | Like 'withAnyVersionM', with an additional type-parameter
-- indicating the oldest version you want to be able to decode
withAnyVersionFromM
:: forall from v c a dec enc m t
. (WithAnyVersionFrom from v a c dec, Alt t, Applicative t, Traversable t, Applicative m, c (a v))
=> Decoder dec enc t a
-> ApplyM m a c
-> enc
-> m (t (Applied c a))
withAnyVersionFromM = withAnyVersion' @(from == v) @from @v @a @c @dec
-- | Pure version of 'withAnyVersionM'.
withAnyVersion
:: forall v c a dec enc t
. (WithAnyVersion v a c dec, c (a v), Alt t, Applicative t, Traversable t)
=> Decoder dec enc t a
-> Apply a c
-> enc
-> t (Applied c a)
withAnyVersion dec action =
runIdentity . withAnyVersionM @v @c @a dec (Identity . action)
-- | Pure version of 'withAnyVersionFromM'.
withAnyVersionFrom
:: forall from v c a dec enc t
. (WithAnyVersionFrom from v a c dec, c (a v), Alt t, Applicative t, Traversable t)
=> Decoder dec enc t a
-> Apply a c
-> enc
-> t (Applied c a)
withAnyVersionFrom dec action =
runIdentity . withAnyVersionFromM @from @v @c @a dec (Identity . action)
type DecodeAnyVersion v w a dec = DecodeAnyVersionFrom V0 v w a dec
type DecodeAnyVersionFrom from v w a dec = DecodeAnyVersion' (from == v) from v w a dec
class DecodeAnyVersion' (eq :: Bool) (from :: V) (v :: V) (w :: V) (a :: V -> Type) dec where
decodeAnyVersion'
:: (Alt t, Applicative t)
=> Decoder dec enc t a
-> enc
-> t (a w)
instance (from ~ v, dec (a from), Upgrade from w a)
=> DecodeAnyVersion' 'True from v w a dec where
decodeAnyVersion' (Decoder decode) bs = upgrade @from @w <$> decode @from bs
instance (DecodeAnyVersion' (VPred v == from) from (VPred v) w a dec, dec (a v), dec (a (VPred v)), Upgrade v w a)
=> DecodeAnyVersion' 'False from v w a dec where
decodeAnyVersion' decoder@(Decoder decode) bs = upgrade @v @w <$> decode @v bs
<!> decodeAnyVersion' @(VPred v == from) @from @(VPred v) @w decoder bs
type WithAnyVersion v a c dec = WithAnyVersionFrom V0 v a c dec
type WithAnyVersionFrom from v a c dec = WithAnyVersion' (from == v) from v a c dec
class WithAnyVersion' (eq :: Bool) (from :: V) (v :: V) (a :: V -> Type) c dec where
withAnyVersion' :: (Applicative m, Alt t, Applicative t, Traversable t, c (a v))
=> Decoder dec enc t a
-> ApplyM m a c
-> enc
-> m (t (Applied c a))
instance (from ~ v, dec (a from), c (a from))
=> WithAnyVersion' 'True from v a c dec where
withAnyVersion' (Decoder decode) action bs = traverse action (decode @from bs)
instance (WithAnyVersion' (VPred v == from) from (VPred v) a c dec, dec (a v), dec (a (VPred v)), c (a v), c (a (VPred v)))
=> WithAnyVersion' 'False from v a c dec where
withAnyVersion' dec@(Decoder decode) action bs = do
res <- traverse action (decode @v bs)
next <- withAnyVersion' @(VPred v == from) @from @(VPred v) @a @c dec action bs
pure (res <!> next)