{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module Data.ByteArray.HexString where
import Data.Aeson (FromJSON (..), ToJSON (..),
Value (..), withText)
import Data.ByteArray (ByteArray, ByteArrayAccess, convert)
import qualified Data.ByteArray as BA (drop, take)
import Data.ByteArray.Encoding (Base (Base16), convertFromBase,
convertToBase)
import Data.ByteString (ByteString)
import Data.Monoid (Monoid, (<>))
import Data.Semigroup (Semigroup)
import Data.String (IsString (..))
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import GHC.Generics (Generic)
newtype HexString = HexString { unHexString :: ByteString }
deriving (Eq, Ord, Generic, Semigroup, Monoid, ByteArrayAccess, ByteArray)
instance Show HexString where
show = ("HexString " ++) . show . format
instance IsString HexString where
fromString = hexString' . fromString
where
hexString' :: ByteString -> HexString
hexString' = either error id . hexString
instance FromJSON HexString where
parseJSON Null = pure (fromBytes ("" :: ByteString))
parseJSON v = withText "HexString" (either fail pure . hexString . encodeUtf8) v
instance ToJSON HexString where
toJSON = String . toText
hexString :: ByteArray ba => ba -> Either String HexString
hexString bs = HexString <$> convertFromBase Base16 bs'
where
hexStart = convert ("0x" :: ByteString)
bs' | BA.take 2 bs == hexStart = BA.drop 2 bs
| otherwise = bs
fromBytes :: ByteArrayAccess ba => ba -> HexString
fromBytes = HexString . convert
toBytes :: ByteArray ba => HexString -> ba
toBytes = convert . unHexString
toText :: HexString -> Text
toText = decodeUtf8 . convertToBase Base16 . unHexString
format :: HexString -> Text
format a = "0x" <> toText a