base32-1.0: src/Data/Base32/Types.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE Trustworthy #-}
-- |
-- Module : Data.Base32.Types
-- Copyright : (c) 2026 Emily Pillmore
-- License : BSD-style
--
-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>
-- Stability : stable
-- Portability : non-portable
--
-- Encoded values tagged with their alphabet and padding policy.
--
-- @since 1.0
--
module Data.Base32.Types
( Alphabet (..)
, StdAlphabet
, HexAlphabet
, Base32
, extractBase32
, assertBase32
) where
import Control.DeepSeq (NFData)
import Data.Functor.Identity (Identity (..))
import Data.Kind (Constraint)
import GHC.TypeLits (ErrorMessage (..), TypeError)
-- | The alphabets and padding forms from RFC 4648 sections 6 and 7.
--
-- @since 1.0
--
data Alphabet = StdPadded | StdUnpadded | HexPadded | HexUnpadded
deriving
( Eq -- ^ @since 1.0
, Ord -- ^ @since 1.0
, Show -- ^ @since 1.0
)
-- | Padded and unpadded forms of the standard alphabet.
--
-- @since 1.0
--
type family StdAlphabet (k :: Alphabet) :: Constraint where
StdAlphabet 'StdPadded = ()
StdAlphabet 'StdUnpadded = ()
StdAlphabet _ = TypeError
( 'Text "Cannot prove base32 value is encoded using the standard alphabet. "
':<>: 'Text "Use the standard encoders or padding-specific parsers."
)
-- | Padded and unpadded forms of the extended-hex alphabet.
--
-- >>> let hex :: HexAlphabet k => Base32 k a -> a; hex = extractBase32
-- >>> hex (assertBase32 @'HexPadded "ADQMS===")
-- "ADQMS==="
-- >>> hex (assertBase32 @'HexUnpadded "ADQMS")
-- "ADQMS"
--
-- @since 1.0
--
type family HexAlphabet (k :: Alphabet) :: Constraint where
HexAlphabet 'HexPadded = ()
HexAlphabet 'HexUnpadded = ()
HexAlphabet _ = TypeError
( 'Text "Cannot prove base32 value is encoded using the extended-hex alphabet. "
':<>: 'Text "Use the hex encoders or padding-specific parsers."
)
-- | A value encoded with the given alphabet and padding. Encoding and parsing establish
-- this provenance. 'assertBase32', record updates, and the derived mapping
-- instances do not validate their results.
--
-- @since 1.0
--
type role Base32 nominal representational
newtype Base32 (alphabet :: Alphabet) a = Base32
{ extractBase32 :: a
-- ^ Extract the encoded representation without decoding it.
--
-- @since 1.0
}
deriving
( Eq -- ^ @since 1.0
, Ord -- ^ @since 1.0
, Show -- ^ @since 1.0
, NFData -- ^ @since 1.0
) via a
deriving
( Functor -- ^ @since 1.0
, Applicative -- ^ @since 1.0
, Monad -- ^ @since 1.0
) via Identity
-- | Assert that a value is Base32-encoded.
--
-- @since 1.0
--
assertBase32 :: forall alphabet a. a -> Base32 alphabet a
assertBase32 = Base32
{-# INLINE assertBase32 #-}
-- $setup
-- >>> :seti -XOverloadedStrings
-- >>> :seti -XDataKinds -XTypeApplications -XTypeFamilies -XFlexibleContexts