{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Database.CQL.IO.Exception where
import Control.Exception (SomeAsyncException (..))
import Control.Monad.Catch
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Text (Text)
import Data.Typeable
import Data.UUID
import Database.CQL.IO.Cluster.Host
import Database.CQL.IO.Connection.Settings
import Database.CQL.Protocol
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Text.Lazy as Lazy
data ResponseError = ResponseError
{ reHost :: !Host
, reTrace :: !(Maybe UUID)
, reWarn :: ![Text]
, reCause :: !Error
} deriving (Show, Typeable)
instance Exception ResponseError
toResponseError :: HostResponse k a b -> Maybe ResponseError
toResponseError (HostResponse h (RsError t w c)) = Just (ResponseError h t w c)
toResponseError _ = Nothing
fromResponseError :: ResponseError -> HostResponse k a b
fromResponseError (ResponseError h t w c) = HostResponse h (RsError t w c)
data HostError
= NoHostAvailable
| HostsBusy
deriving Typeable
instance Exception HostError
instance Show HostError where
show NoHostAvailable = "cql-io: no host available"
show HostsBusy = "cql-io: hosts busy"
data ConnectionError
= ConnectionClosed !InetAddr
| ConnectTimeout !InetAddr
| ResponseTimeout !InetAddr
deriving Typeable
instance Exception ConnectionError
instance Show ConnectionError where
show (ConnectionClosed i) = "cql-io: connection closed: " ++ show i
show (ConnectTimeout i) = "cql-io: connect timeout: " ++ show i
show (ResponseTimeout i) = "cql-io: response timeout: " ++ show i
data ProtocolError where
UnexpectedResponse :: Host -> Response k a b -> ProtocolError
UnexpectedQueryId :: QueryId k a b -> ProtocolError
UnsupportedCompression :: CompressionAlgorithm -> [CompressionAlgorithm] -> ProtocolError
SerialiseError :: String -> ProtocolError
ParseError :: String -> ProtocolError
deriving instance Typeable ProtocolError
instance Exception ProtocolError
instance Show ProtocolError where
show e = showString "cql-io: protocol error: " . case e of
ParseError x ->
showString "parse error: " . showString x
SerialiseError x ->
showString "serialise error: " . showString x
UnsupportedCompression x cc ->
showString "unsupported compression: " . shows x .
showString ", expected one of " . shows cc
UnexpectedQueryId i ->
showString "unexpected query ID: " . shows i
UnexpectedResponse h r -> showString "unexpected response: " .
shows h . showString ": " . shows (f r)
$ ""
where
f :: Response k a b -> Response k a NoShow
f (RsError a b c) = RsError a b c
f (RsReady a b c) = RsReady a b c
f (RsAuthenticate a b c) = RsAuthenticate a b c
f (RsAuthChallenge a b c) = RsAuthChallenge a b c
f (RsAuthSuccess a b c) = RsAuthSuccess a b c
f (RsSupported a b c) = RsSupported a b c
f (RsResult a b c) = RsResult a b (g c)
f (RsEvent a b c) = RsEvent a b c
g :: Result k a b -> Result k a NoShow
g VoidResult = VoidResult
g (RowsResult a b ) = RowsResult a (map (const NoShow) b)
g (SetKeyspaceResult a ) = SetKeyspaceResult a
g (SchemaChangeResult a ) = SchemaChangeResult a
g (PreparedResult (QueryId a) b c) = PreparedResult (QueryId a) b c
data NoShow = NoShow deriving Show
data HashCollision = HashCollision !Lazy.Text !Lazy.Text
deriving Typeable
instance Exception HashCollision
instance Show HashCollision where
show (HashCollision a b) = showString "cql-io: hash collision: "
. shows a
. showString " "
. shows b
$ ""
data AuthenticationError
= AuthenticationRequired !AuthMechanism
| UnexpectedAuthenticationChallenge !AuthMechanism !AuthChallenge
instance Exception AuthenticationError
instance Show AuthenticationError where
show (AuthenticationRequired a)
= showString "cql-io: authentication required: "
. shows a
$ ""
show (UnexpectedAuthenticationChallenge n c)
= showString "cql-io: unexpected authentication challenge: '"
. shows c
. showString "' using mechanism '"
. shows n
. showString "'"
$ ""
recover :: forall m a. MonadCatch m => m a -> a -> m a
recover io val = try io >>= either fallback return
where
fallback :: SomeException -> m a
fallback e = case fromException e of
Just (SomeAsyncException _) -> throwM e
Nothing -> return val
{-# INLINE recover #-}
ignore :: MonadCatch m => m () -> m ()
ignore io = recover io ()
{-# INLINE ignore #-}
tryAll :: forall m a b. MonadCatch m => NonEmpty a -> (a -> m b) -> m b
tryAll (a :| []) f = f a
tryAll (a :| aa) f = try (f a) >>= either next return
where
next :: SomeException -> m b
next e = case fromException e of
Just (SomeAsyncException _) -> throwM e
Nothing -> tryAll (NonEmpty.fromList aa) f