rds-data-0.1.1.3: src/Data/RdsData/Decode/Row.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module Data.RdsData.Decode.Row
( DecodeRow(..)
, ToRows(..)
, integer
, int
, int8
, int16
, int32
, int64
, bool
, double
, string
, text
, lazyText
, word
, word8
, word16
, word32
, word64
, bytestring
, lazyBytestring
, base64Text
, lazyBase64Text
, timeOfDay
, day
, ulid
, utcTime
, uuid
, ignore
, json
, maybe
, column
, decodeRow
, decodeRows
) where
import Control.Monad
import Control.Monad.Except
import Control.Monad.State
import Data.ByteString (ByteString)
import Data.Functor.Identity (Identity)
import Data.Int
import Data.RdsData.Decode.Value (DecodeValue)
import Data.RdsData.Internal.Show
import Data.RdsData.Types.Value
import Data.Text
import Data.Time
import Data.ULID (ULID)
import Data.UUID (UUID)
import Data.Word
import Prelude hiding (maybe)
import qualified Data.Aeson as J
import qualified Data.ByteString.Lazy as LBS
import Data.RdsData.Decode.ToRows
import qualified Data.RdsData.Decode.Value as DV
import qualified Data.RdsData.Internal.Convert as CONV
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import qualified Data.UUID as UUID
newtype DecodeRow a = DecodeRow
{ run :: ExceptT Text (StateT [Value] Identity) a
}
deriving (Applicative, Functor, Monad, MonadState [Value], MonadError Text)
instance MonadFail DecodeRow where
fail = DecodeRow . throwError . pack
maybe :: DecodeRow a -> DecodeRow (Maybe a)
maybe r = do
cs <- get
case cs of
ValueOfNull : vs -> do
put vs
pure Nothing
_ -> Just <$> r
decodeRowValue :: ()
=> MonadError Text m
=> DecodeValue a
-> Value
-> m a
decodeRowValue decoder v =
case decoder.decodeValue v of
Right a -> pure a
Left e -> throwError $ "Failed to decode Value: " <> e
column :: ()
=> DecodeValue a
-> DecodeRow a
column decoder = do
cs <- get
case cs of
v : vs -> do
s <- decodeRowValue decoder v
put vs
pure s
[] -> do
throwError "Expected RdsText, but got no more values in row."
integer :: DecodeRow Integer
integer =
column DV.integer
int :: DecodeRow Int
int =
column DV.int
int8 :: DecodeRow Int8
int8 =
column DV.int8
int16 :: DecodeRow Int16
int16 =
column DV.int16
int32 :: DecodeRow Int32
int32 =
column DV.int32
int64 :: DecodeRow Int64
int64 =
column DV.int64
word :: DecodeRow Word
word =
column DV.word
word8 :: DecodeRow Word8
word8 =
column DV.word8
word16 :: DecodeRow Word16
word16 =
column DV.word16
word32 :: DecodeRow Word32
word32 =
column DV.word32
word64 :: DecodeRow Word64
word64 =
column DV.word64
text :: DecodeRow Text
text =
column DV.text
lazyText :: DecodeRow LT.Text
lazyText =
column DV.lazyText
bool :: DecodeRow Bool
bool =
column DV.bool
double :: DecodeRow Double
double =
column DV.double
bytestring :: DecodeRow ByteString
bytestring =
column DV.bytestring
lazyBytestring :: DecodeRow LBS.ByteString
lazyBytestring =
column DV.lazyBytestring
base64Text :: DecodeRow ByteString
base64Text =
column DV.base64Text
lazyBase64Text :: DecodeRow LBS.ByteString
lazyBase64Text =
column DV.lazyBase64Text
string :: DecodeRow String
string =
column DV.string
json :: DecodeRow J.Value
json =
column DV.json
timeOfDay :: DecodeRow TimeOfDay
timeOfDay = do
t <- text
case parseTimeM True defaultTimeLocale "%H:%M:%S%Q" (T.unpack t) of
Just a -> pure a
Nothing -> throwError $ "Failed to parse TimeOfDay: " <> tshow t
ulid :: DecodeRow ULID
ulid = do
t <- text
case CONV.textToUlid t of
Right a -> pure a
Left msg -> throwError $ "Failed to parse ULID: " <> msg
utcTime :: DecodeRow UTCTime
utcTime = do
t <- text
case parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M:%S" (T.unpack t) of
Just a -> pure a
Nothing -> throwError $ "Failed to parse UTCTime: " <> tshow t
uuid :: DecodeRow UUID
uuid = do
t <- text
case UUID.fromString (T.unpack t) of
Just a -> pure a
Nothing -> throwError $ "Failed to parse UUID: " <> tshow t
day :: DecodeRow Day
day = do
t <- text
case parseTimeM True defaultTimeLocale "%Y-%m-%d" (T.unpack t) of
Just a -> pure a
Nothing -> throwError $ "Failed to parse Day: " <> tshow t
ignore :: DecodeRow ()
ignore =
void $ column DV.rdsValue
decodeRow :: DecodeRow a -> [Value] -> Either Text a
decodeRow r = evalState (runExceptT r.run)
decodeRows :: ToRows res => DecodeRow a -> res -> Either Text [a]
decodeRows r = traverse (decodeRow r) . toRows