    {-# language DataKinds #-}
    {-# language PolyKinds #-}
    {-# language RankNTypes #-}
    {-# language TypeFamilies #-}
    {-# language UndecidableInstances #-}
    -- |
    -- Module       : Data.ByteString.Base64.Types
    -- Copyright    : (c) 2019-2023 Emily Pillmore
    -- License      : BSD-style
    --
    -- Maintainer   : Emily Pillmore <emilypi@cohomolo.gy>,
    --                sofia-m-a <https://github.com/sofia-m-a>
    -- Stability    : stable
    -- Portability  : non-portable
    --
    -- This module contains the 'Base64' type definition, 'Alphabet'
    -- datatype, alphabet constraints, and various quality of life
    -- combinators for working with 'Base64'-wrapped data.
    --
    module Data.Base64.Types
    ( Alphabet(..)
    , type Base64
    , assertBase64
    , extractBase64
    , coerceBase64
    , type UrlAlphabet
    , type StdAlphabet
    , type NonStandardAlphabet
    ) where

    import Data.Base64.Types.Internal (Alphabet(..), Base64(..))
    import Data.Coerce (coerce)
    import Data.Kind

    import GHC.TypeLits

    -- | Assert the provenance of a value encoded in a particular
    -- base64 alphabet.
    --
    -- /Warning/: This is a blind assertion that a particular
    -- value is base64 encoded in some alphabet. If you are not
    -- sure of the provenance of the value, you may experience
    -- odd behavior when attempting to decode. Use at your own
    -- risk. If I see any issues logged on this project from
    -- negligent use of this or 'coerceBase64', I will
    -- smite you.
    --
    assertBase64 :: forall k a. a -> Base64 k a
    assertBase64 :: forall (k :: Alphabet) a. a -> Base64 k a
    assertBase64 = a -> Base64 k a
    forall (k :: Alphabet) a. a -> Base64 k a
    Base64

    -- | Forget the provenance of a base64-encoded value.
    --
    extractBase64 :: Base64 k a -> a
    extractBase64 :: forall (k :: Alphabet) a. Base64 k a -> a
    extractBase64 (Base64 a
    a) = a
    a

    -- | Coerce the alphabet of a base64-encoded bytestring
    --
    -- /Warning/: This is a blind assertion that a particular
    -- value is base64 encoded in some alphabet. If you are not
    -- sure of the provenance of the value, you may experience
    -- odd behavior when attempting to decode. Use at your own
    -- risk. If I see any issues logged on this project from
    -- negligent use of this or 'assertBase64', I will
    -- smite you.
    --
    coerceBase64 :: forall j k a. Base64 k a -> Base64 j a
    coerceBase64 :: forall (j :: Alphabet) (k :: Alphabet) a. Base64 k a -> Base64 j a
    coerceBase64 = Base64 k a -> Base64 j a
    forall a b. Coercible a b => a -> b
    coerce

    -- | The type family of Url-safe alphabets
    --
    -- This type family defines the union of compatible Url-safe base64 types.
    -- To write a function that is parametric over such types, issue a
    -- constraint like `forall k. UrlAlphabet k`.
    --
    type family UrlAlphabet k :: Constraint where
      UrlAlphabet 'UrlPadded = ()
      UrlAlphabet 'UrlUnpadded = ()
      UrlAlphabet _ = TypeError
        ( 'Text "Cannot prove base64 value is encoded using the url-safe \
                \alphabet. Please re-encode using the url-safe encoders, or use \
                \a lenient decoder for the url-safe alphabet instead."
        )

    -- | The type family of standard alphabets
    --
    -- This type family defines the union of compatible standard
    -- alphabet base64 types
    --
    type family StdAlphabet k :: Constraint where
      StdAlphabet 'StdPadded = ()
      StdAlphabet _ = TypeError
        ( 'Text "Cannot prove base64 value is encoded using the std \
                \alphabet. Please re-encode using the std encoders, or use \
                \a lenient decoder for the std alphabet instead."
        )

    -- | The type family of non-standard alphabets
    --
    -- Only untyped variants of encodeBase64/decodeBase64 can interact with this
    -- type family, in addition to assertion\/coercion\/extraction of
    -- these types of values.
    --
    type family NonStandardAlphabet k :: Constraint where
      NonStandardAlphabet 'NonStandard = ()
      NonStandardAlphabet _ = TypeError
        ( 'Text "Cannot prove base64 value is encoded using a non-standard \
                \alphabet. Please re-encode and assert/coerce the alphabet, \
                \and use a lenient decoder."
        )