rds-data-0.0.0.1: src/Data/RdsData/Decode/Array.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
{- HLINT ignore "Use <&>" -}
module Data.RdsData.Decode.Array
( DecodeArray(..)
, decodeArrayFailedMessage
, arrays
, bools
, doubles
, integers
, texts
, days
, int16s
, int32s
, int64s
, int8s
, ints
, jsons
, lazyTexts
, strings
, timesOfDay
, ulids
, utcTimes
, uuids
, word16s
, word32s
, word64s
, word8s
, words
) where
import Control.Applicative
import Data.Int
import Data.RdsData.Internal.Aeson
import Data.RdsData.Types.Array
import Data.Text (Text)
import Data.Time
import Data.ULID (ULID)
import Data.UUID (UUID)
import Data.Word
import Prelude hiding (maybe, null, words)
import qualified Data.Aeson as J
import qualified Data.RdsData.Internal.Convert as CONV
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as LT
import qualified Data.UUID as UUID
import qualified Prelude as P
newtype DecodeArray a = DecodeArray
{ decodeArray :: Array -> Either Text a
} deriving Functor
instance Applicative DecodeArray where
pure a = DecodeArray \_ -> Right a
DecodeArray f <*> DecodeArray a = DecodeArray \v -> f v <*> a v
instance Alternative DecodeArray where
empty = DecodeArray \_ -> Left "empty"
DecodeArray a <|> DecodeArray b = DecodeArray \v ->
either (const (b v)) Right (a v)
instance Monad DecodeArray where
DecodeArray a >>= f = DecodeArray \v -> do
a' <- a v
decodeArray (f a') v
--------------------------------------------------------------------------------
decodeArrayFailedMessage :: Text -> Text -> Maybe Text -> Array -> Text
decodeArrayFailedMessage item type_ reason value =
mconcat
[ "Failed to decode " <> item <> " of type " <> type_ <> " from Array of " <> toJsonText value
, P.maybe "" (" because " <>) reason
]
--------------------------------------------------------------------------------
integers :: DecodeArray [Integer]
integers =
DecodeArray \v ->
case v of
ArrayOfIntegers es -> Right es
_ -> Left $ decodeArrayFailedMessage "integers" "ArrayOfIntegers" Nothing v
texts :: DecodeArray [Text]
texts =
DecodeArray \v ->
case v of
ArrayOfTexts es -> Right es
_ -> Left $ decodeArrayFailedMessage "texts" "ArrayOfStrings" Nothing v
bools :: DecodeArray [Bool]
bools =
DecodeArray \v ->
case v of
ArrayOfBools es -> Right es
_ -> Left $ decodeArrayFailedMessage "bools" "ArrayOfBooleans" Nothing v
doubles :: DecodeArray [Double]
doubles =
DecodeArray \v ->
case v of
ArrayOfDoubles es -> Right es
_ -> Left $ decodeArrayFailedMessage "doubles" "ArrayOfDoubles" Nothing v
arrays :: DecodeArray [Array]
arrays =
DecodeArray \v ->
case v of
ArrayOfArrays es -> Right es
_ -> Left $ decodeArrayFailedMessage "arrays" "ArrayOfArrays" Nothing v
--------------------------------------------------------------------------------
ints :: DecodeArray [Int]
ints =
fmap fromIntegral <$> integers
int8s :: DecodeArray [Int8]
int8s =
fmap fromIntegral <$> integers
int16s :: DecodeArray [Int16]
int16s =
fmap fromIntegral <$> integers
int32s :: DecodeArray [Int32]
int32s =
fmap fromIntegral <$> integers
int64s :: DecodeArray [Int64]
int64s =
fmap fromIntegral <$> integers
words :: DecodeArray [Word]
words =
fmap fromIntegral <$> integers
word8s :: DecodeArray [Word8]
word8s =
fmap fromIntegral <$> integers
word16s :: DecodeArray [Word16]
word16s =
fmap fromIntegral <$> integers
word32s :: DecodeArray [Word32]
word32s =
fmap fromIntegral <$> integers
word64s :: DecodeArray [Word64]
word64s =
fmap fromIntegral <$> integers
lazyTexts :: DecodeArray [LT.Text]
lazyTexts =
fmap LT.fromStrict <$> texts
strings :: DecodeArray [String]
strings =
fmap T.unpack <$> texts
jsons :: DecodeArray [J.Value]
jsons = do
ts <- texts
case traverse (J.eitherDecodeStrict' . T.encodeUtf8) ts of
Right js -> pure js
Left e -> DecodeArray \_ -> Left $ "Failed to decode JSON: " <> T.pack e
timesOfDay :: DecodeArray [TimeOfDay]
timesOfDay = do
ts <- texts
case traverse (parseTimeM True defaultTimeLocale "%H:%M:%S". T.unpack) ts of
Just tod -> pure tod
Nothing -> DecodeArray \_ -> Left "Failed to decode TimeOfDay"
utcTimes :: DecodeArray [UTCTime]
utcTimes = do
ts <- texts
case traverse (parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M:%S" . T.unpack) ts of
Just utct -> pure utct
Nothing -> DecodeArray \_ -> Left "Failed to decode UTCTime"
days :: DecodeArray [Day]
days = do
ts <- texts
case traverse (parseTimeM True defaultTimeLocale "%Y-%m-%d" . T.unpack) ts of
Just d -> pure d
Nothing -> DecodeArray \_ -> Left "Failed to decode Day"
-- | Decode an array of ULIDs
-- ULIDs are encoded as strings in the database and have have better database performance
-- than UUIDs stored as strings in the database.
ulids :: DecodeArray [ULID]
ulids = do
ts <- texts
case traverse CONV.textToUlid ts of
Right u -> pure u
Left msg -> DecodeArray \_ -> Left $ "Failed to decode UUID: " <> msg
uuids :: DecodeArray [UUID]
uuids = do
ts <- texts
case traverse (UUID.fromString . T.unpack) ts of
Just u -> pure u
Nothing -> DecodeArray \_ -> Left "Failed to decode UUID"