{-|
    Use this module if you like to write simple scripts, but you prefer to use
    'EitherT' to handle errors rather than @Control.Exception@.

> import Control.Error
>
> main = runScript $ do
>     str <- tryIO getLine
>     n   <- tryRead "Read failed" str
>     tryIO $ print (n + 1)
-}

module Control.Error.Script (
    -- * The Script Monad
    Script,
    runScript,
    tryMaybe,
    tryEither,
    tryIO
    ) where

import Control.Exception
import Control.Monad.Trans.Either
import Control.Error.Util
import Data.EitherR
import System.IO
import System.Exit

-- | An 'IO' action that can fail with a 'String' error message
type Script = EitherT String IO

{-|
    Runs the 'Script' monad

    Prints the first error to 'stderr' and exits with 'exitFailure'
-}
runScript :: Script a -> IO a
runScript s = do
    e <- runEitherT s
    case e of
        Left  e -> do
            hPutStrLn stderr e
            exitFailure
        Right a -> return a

-- | A 'Maybe' that fails in the 'Script' monad
tryMaybe :: String -> Maybe a -> Script a
tryMaybe str = tryEither . note str

-- | An 'Either' that fails in the 'Script' monad
tryEither :: Either String r -> Script r
tryEither = liftEither

-- | 'tryIO' is like 'lift', except it converts exceptions to the 'Script' monad
tryIO :: IO a -> Script a
tryIO io = EitherT . fmap (fmapL show)
                   $ (try :: IO a -> IO (Either SomeException a)) io