-- |
-- Module      : Foundation.Primitives.Types.AsciiString
-- License     : BSD-style
-- Maintainer  : Haskell Foundation
-- Stability   : experimental
-- Portability : portable
--
-- A AsciiString type backed by a `ASCII` encoded byte array and all the necessary
-- functions to manipulate the string.
--
{-# LANGUAGE BangPatterns               #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash                  #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE FlexibleContexts           #-}
module Basement.Types.AsciiString
    ( AsciiString(..)
    , MutableAsciiString(..)
    -- * Binary conversion
    , fromBytesUnsafe
    , fromBytes
    ) where

import           Basement.Compat.Base
import           Basement.Types.Char7
import           Basement.UArray.Base
import qualified Basement.Types.Char7 as Char7
import qualified Basement.UArray as A (all, unsafeRecast)

-- | Opaque packed array of characters in the ASCII encoding
newtype AsciiString = AsciiString { toBytes :: UArray Char7 }
    deriving (Typeable, Monoid, Eq, Ord)

newtype MutableAsciiString st = MutableAsciiString (MUArray Char7 st)
    deriving (Typeable)

instance Show AsciiString where
    show = fmap Char7.toChar . toList
instance IsString AsciiString where
    fromString = fromList . fmap Char7.fromCharMask
instance IsList AsciiString where
    type Item AsciiString = Char7
    fromList = AsciiString . fromList
    toList (AsciiString chars) = toList chars

-- | Convert a Byte Array representing ASCII data directly to an AsciiString without checking for ASCII validity
--
-- If the input contains invalid Char7 value (anything above 0x7f),
-- it will trigger runtime async errors when processing data.
--
-- In doubt, use 'fromBytes'
fromBytesUnsafe :: UArray Word8 -> AsciiString
fromBytesUnsafe = AsciiString . A.unsafeRecast

-- | Convert a Byte Array representing ASCII checking validity.
--
-- If the byte array is not valid, then Nothing is returned
fromBytes :: UArray Word8 -> Maybe AsciiString
fromBytes arr
    | A.all (\x -> x < 0x80) arr = Just $ AsciiString $ A.unsafeRecast arr
    | otherwise                  = Nothing