ron-0.12: lib/RON/Error.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module RON.Error (
Error (..),
MonadE,
correct,
errorContext,
liftEither,
liftEitherString,
liftMaybe,
throwError,
throwErrorString,
throwErrorText,
tryIO,
) where
import RON.Prelude
import Control.Exception (SomeException, try)
import Data.String (IsString, fromString)
import qualified Data.Text as Text
import GHC.Stack (callStack, getCallStack, prettySrcLoc)
import qualified Text.Show
data Error = Error{description :: Text, reasons :: [Error]}
deriving (Eq)
instance Show Error where
show =
unlines . go
where
go Error{description, reasons} =
Text.unpack description : foldMap (map (" " ++) . go) reasons
instance Exception Error
instance IsString Error where
fromString s = Error (fromString s) []
type MonadE = MonadError Error
errorContext :: MonadE m => Text -> m a -> m a
errorContext ctx action = action `catchError` \e -> throwError $ Error ctx [e]
liftMaybe :: MonadE m => Text -> Maybe a -> m a
liftMaybe msg = maybe (throwErrorText msg) pure
liftEitherString :: (MonadError e m, IsString e) => Either String a -> m a
liftEitherString = either throwErrorString pure
throwErrorText :: MonadE m => Text -> m a
throwErrorText msg = throwError $ Error msg []
throwErrorString :: (MonadError e m, IsString e) => String -> m a
throwErrorString = throwError . fromString
correct :: MonadError e m => a -> m a -> m a
correct def action =
action
`catchError` \_e ->
-- TODO(2019-08-06, cblp) $logWarnSH e
pure def
tryIO :: (MonadE m, MonadIO m, HasCallStack) => IO a -> m a
tryIO action = do
e <- liftIO $ try action
case e of
Right a -> pure a
Left exc -> do
let
description =
case getCallStack callStack of
[] -> "tryIO"
(f, loc) : _ -> Text.pack $ f ++ ", called at " ++ prettySrcLoc loc
throwError
Error{description, reasons = [fromString (show (exc :: SomeException))]}