packages feed

hasql-0.20.1: library/Hasql/Core/Protocol/Parse/Responses.hs

module Hasql.Core.Protocol.Parse.Responses where

import Hasql.Prelude hiding (fail)
import Hasql.Core.Protocol.Parse.Primitives
import Hasql.Core.Model
import Ptr.Parse
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as A
import qualified Data.Vector as D
import qualified Hasql.Core.MessageTypePredicates as C
import qualified Hasql.Core.NoticeFieldTypes as E
import qualified Hasql.Core.ParseDataRow as F


{-# INLINE responseBody #-}
responseBody :: Word8 -> Parse (Maybe Response)
responseBody type_ =
  {-# SCC "responseBody" #-} 
  if
    | C.dataRow type_ -> (Just . DataRowResponse) <$> allBytes
    | C.commandComplete type_ -> Just . CommandCompleteResponse <$> commandCompleteBody
    | C.readyForQuery type_ -> fmap (Just . ReadyForQueryResponse) readyForQueryBody
    | C.parseComplete type_ -> pure (Just ParseCompleteResponse)
    | C.bindComplete type_ -> pure (Just BindCompleteResponse)
    | C.emptyQuery type_ -> pure (Just EmptyQueryResponse)
    | C.notification type_ -> Just <$> notificationBody NotificationResponse
    | C.error type_ -> Just <$> errorResponseBody ErrorResponse
    | C.authentication type_ -> fmap (Just . AuthenticationResponse) authenticationBody
    | C.parameterStatus type_ -> Just <$> parameterStatusBody ParameterStatusResponse
    | True -> pure Nothing

{-# INLINE unparsedFieldsOfDataRowBody #-}
unparsedFieldsOfDataRowBody :: Parse (Vector (Maybe ByteString))
unparsedFieldsOfDataRowBody =
  {-# SCC "unparsedFieldsOfDataRowBody" #-} 
  do
    amountOfColumns <- beWord16
    D.replicateM (fromIntegral amountOfColumns) sizedBytes

{-# INLINE dataRowBody #-}
dataRowBody :: F.ParseDataRow row -> Parse row
dataRowBody (F.ParseDataRow rowLength parse) =
  do
    amountOfColumns <- beWord16
    if fromIntegral amountOfColumns == rowLength
      then parse
      else fail (fromString
        (showString "Invalid amount of columns: "
          (shows amountOfColumns
            (showString ", expecting "
              (show rowLength)))))

{-# INLINE commandCompleteBody #-}
commandCompleteBody :: Parse Int
commandCompleteBody =
  {-# SCC "commandCompleteBody" #-} 
  do
    header <- bytesWhile byteIsUpperLetter
    word8
    case header of
      "INSERT" -> skipWhile byteIsDigit *> word8 *> unsignedASCIIIntegral <* word8
      _ -> unsignedASCIIIntegral <* word8
  where
    byteIsUpperLetter byte =
      byte - 65 <= 25
    byteIsDigit byte =
      byte - 48 <= 9

{-# INLINE readyForQueryBody #-}
readyForQueryBody :: Parse TransactionStatus
readyForQueryBody =
  {-# SCC "readyForQueryBody" #-} 
  do
    statusByte <- word8
    case statusByte of
      73 -> return IdleTransactionStatus
      84 -> return ActiveTransactionStatus
      69 -> return FailedTransactionStatus
      _ -> fail ("Unexpected transaction status byte: " <> (fromString . show) statusByte)

{-# INLINE notificationBody #-}
notificationBody :: (Word32 -> ByteString -> ByteString -> result) -> Parse result
notificationBody result =
  {-# SCC "notificationBody" #-} 
  result <$> beWord32 <*> nullTerminatedBytes <*> nullTerminatedBytes

{-# INLINE errorResponseBody #-}
errorResponseBody :: (ByteString -> ByteString -> result) -> Parse result
errorResponseBody result =
  {-# SCC "errorResponseBody" #-} 
  iterate Nothing Nothing
  where
    iterate code message =
      element <|> end
      where
        element =
          join (noticeField (\type_ payload ->
            if
              | type_ == E.code -> iterate (Just payload) message
              | type_ == E.message -> iterate code (Just payload)
              | True -> iterate code message))
        end =
          do
            word8
            case code of
              Just code -> case message of
                Just message -> return (result code message)
                _ -> fail "The \"message\" field is missing"
              _ -> fail "The \"code\" field is missing"

{-# INLINE noticeField #-}
noticeField :: (Word8 -> ByteString -> result) -> Parse result
noticeField result =
  {-# SCC "noticeField" #-} 
  result <$> word8 <*> nullTerminatedBytes

{-# INLINE authenticationBody #-}
authenticationBody :: Parse AuthenticationStatus
authenticationBody =
  {-# SCC "authenticationBody" #-} 
  do
    status <- beWord32
    case status of
      0 -> return OkAuthenticationStatus
      3 -> return NeedClearTextPasswordAuthenticationStatus
      5 -> do
        salt <- bytes 4
        return (NeedMD5PasswordAuthenticationStatus salt)
      _ -> fail ("Unsupported authentication status: " <> (fromString . show) status)

{-# INLINE parameterStatusBody #-}
parameterStatusBody :: (ByteString -> ByteString -> result) -> Parse result
parameterStatusBody result =
  {-# SCC "parameterStatusBody" #-} 
  result <$> nullTerminatedBytes <*> nullTerminatedBytes