{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
module Data.TypedEncoding.Common.Class.Superset where
import Data.TypedEncoding.Common.Util.TypeLits
import Data.TypedEncoding.Common.Types (Enc(..)
, EncodeEx(..)
, Encoding
, getPayload
, runEncoding'
, toEncoding
, AlgNm)
import Data.TypedEncoding.Combinators.Unsafe (withUnsafeCoerce)
import GHC.TypeLits
import Data.Symbol.Ascii
import Data.Either (isLeft)
type family IsSuperset (y :: Symbol) (x :: Symbol) :: Bool where
IsSuperset "r-ASCII" "r-ASCII" = 'True
IsSuperset "r-UTF8" "r-ASCII" = 'True
IsSuperset "r-UTF8" "r-UTF8" = 'True
IsSuperset "r-CHAR8" "r-ASCII" = 'True
IsSuperset "r-CHAR8" "r-ByteRep" = 'True
IsSuperset "r-UNICODE.D76" "r-UNICODE.D76" = 'True
IsSuperset "r-UNICODE.D76" "r-ASCII" = 'True
IsSuperset "r-UNICODE.D76" x = Or (IsSuperset "r-CHAR8" x) (IsSupersetOpen "r-UNICODE.D76" x (TakeUntil x ":") (ToList x))
IsSuperset "r-CHAR8" x = Or (IsSuperset "r-ASCII" x) (IsSupersetOpen "r-CHAR8" x (TakeUntil x ":") (ToList x))
IsSuperset y x = IsSupersetOpen y x (TakeUntil x ":") (ToList x)
type family IsSupersetOpen (big :: Symbol) (nm :: Symbol) (alg :: Symbol) (nmltrs :: [Symbol]) :: Bool
type Superset big small = (IsSuperset big small ~ 'True)
injectInto :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': xs) c str -> Enc (y ': xs) c str
injectInto = withUnsafeCoerce id
propSuperset' :: forall algb algs b s str . (Superset b s, Eq str)
=>
Encoding (Either EncodeEx) b algb () str
-> Encoding (Either EncodeEx) s algs () str
-> str
-> Bool
propSuperset' = propSupersetCheck @algb @algs
propSuperset_ :: forall b s str algb algs. (Superset b s, Eq str, AlgNm b ~ algb, AlgNm s ~ algs) => Encoding (Either EncodeEx) b algb () str -> Encoding (Either EncodeEx) s algs () str -> str -> Bool
propSuperset_ = propSupersetCheck @algb @algs
propSupersetCheck :: forall algb algs b s str . (Eq str)
=>
Encoding (Either EncodeEx) b algb () str
-> Encoding (Either EncodeEx) s algs () str
-> str
-> Bool
propSupersetCheck encb encs str =
case (isLeft rb, isLeft rs) of
(True, False) -> False
(False, False) -> pb == ps
_ -> True
where
rb = runEncoding' @algb encb . toEncoding () $ str
rs = runEncoding' @algs encs . toEncoding () $ str
pb = either (const str) id $ getPayload <$> rb
ps = either (const str) id $ getPayload <$> rs
class EncodingSuperset (enc :: Symbol) where
type EncSuperset enc :: Symbol
implEncInto :: forall xs c str . Enc (enc ': xs) c str -> Enc (EncSuperset enc ': enc ': xs) c str
implEncInto = withUnsafeCoerce id
{-# WARNING implEncInto "Using this method at the call site may not be backward compatible between minor version upgrades, use _encodesInto instead." #-}
_encodesInto :: forall y enc xs c str r . (IsSuperset y r ~ 'True, EncodingSuperset enc, r ~ EncSuperset enc) => Enc (enc ': xs) c str -> Enc (y ': enc ': xs) c str
_encodesInto = injectInto . implEncInto
propEncodesInto' :: forall algb algr b r str . (EncodingSuperset b, r ~ EncSuperset b, Eq str) => Encoding (Either EncodeEx) b algb () str -> Encoding (Either EncodeEx) r algr () str -> str -> Bool
propEncodesInto' encb encr str =
case runEncoding' @algb encb . toEncoding () $ str of
Right r -> case runEncoding' @algr encr . toEncoding () $ getPayload r of
Left _ -> False
Right _ -> True
Left _ -> True
propEncodesInto_ :: forall b r str algb algr. (
EncodingSuperset b
, r ~ EncSuperset b
, Eq str
, AlgNm b ~ algb
, AlgNm r ~ algr
) => Encoding (Either EncodeEx) b algb () str
-> Encoding (Either EncodeEx) r algr () str
-> str
-> Bool
propEncodesInto_ = propEncodesInto' @algb @algr
propCompEncoding :: forall algb algr b r str . Encoding (Either EncodeEx) b algb () str -> Encoding (Either EncodeEx) r algr () str -> str -> Bool
propCompEncoding encb encr str =
case rr of
Left _ -> True
Right _ -> case runEncoding' @algb encb . toEncoding () $ str of
Right _ -> True
Left _ -> False
where
rr = runEncoding' @algr encr . toEncoding () $ str
class AllEncodeInto (superset :: Symbol) (encnms :: [Symbol]) where
instance {-# OVERLAPPING #-} AllEncodeInto "r-CHAR8" '[]
instance (AllEncodeInto "r-CHAR8" xs, IsSuperset "r-CHAR8" r ~ 'True, EncodingSuperset enc, r ~ EncSuperset enc) => AllEncodeInto "r-CHAR8" (enc ': xs)
instance {-# OVERLAPPING #-} AllEncodeInto "r-UNICODE.D76" '[]
instance (AllEncodeInto "r-UNICODE.D76" xs, IsSuperset "r-UNICODE.D76" r ~ 'True, EncodingSuperset enc, r ~ EncSuperset enc) => AllEncodeInto "r-UNICODE.D76" (enc ': xs)
instance {-# OVERLAPPING #-} AllEncodeInto "r-UTF8" '[]
instance (AllEncodeInto "r-UTF8" xs, IsSuperset "r-UTF8" r ~ 'True, EncodingSuperset enc, r ~ EncSuperset enc) => AllEncodeInto "r-UTF8" (enc ': xs)
tstChar8Encodable :: forall nms . AllEncodeInto "r-CHAR8" nms => String
tstChar8Encodable = "I am CHAR8 encodable"
tstD76Encodable :: forall nms . AllEncodeInto "r-UNICODE.D76" nms => String
tstD76Encodable = "I can be a valid text"
tstUTF8Encodable :: forall nms . AllEncodeInto "r-UTF8" nms => String
tstUTF8Encodable = "I am a valid UTF8"