{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Language.PureScript.Make.Monad ( -- * Implementation of Make API using files on disk Make(..) , runMake , makeIO , readTextFile ) where import Prelude import Control.Monad.Base (MonadBase(..)) import Control.Monad.Error.Class (MonadError(..)) import Control.Monad.IO.Class import Control.Monad.Logger import Control.Monad.Reader (MonadReader(..), ReaderT(..)) import Control.Monad.Trans.Control (MonadBaseControl(..)) import Control.Monad.Trans.Except import Control.Monad.Writer.Class (MonadWriter(..)) import qualified Data.ByteString.Lazy as B import Language.PureScript.AST import Language.PureScript.Errors import Language.PureScript.Options import System.IO.Error (tryIOError) -- | A monad for running make actions newtype Make a = Make { unMake :: ReaderT Options (ExceptT MultipleErrors (Logger MultipleErrors)) a } deriving (Functor, Applicative, Monad, MonadIO, MonadError MultipleErrors, MonadWriter MultipleErrors, MonadReader Options) instance MonadBase IO Make where liftBase = liftIO instance MonadBaseControl IO Make where type StM Make a = Either MultipleErrors a liftBaseWith f = Make $ liftBaseWith $ \q -> f (q . unMake) restoreM = Make . restoreM -- | Execute a 'Make' monad, returning either errors, or the result of the compile plus any warnings. runMake :: Options -> Make a -> IO (Either MultipleErrors a, MultipleErrors) runMake opts = runLogger' . runExceptT . flip runReaderT opts . unMake -- | Run an 'IO' action in the 'Make' monad, by specifying how IO errors should -- be rendered as 'ErrorMessage' values. makeIO :: (IOError -> ErrorMessage) -> IO a -> Make a makeIO f io = do e <- liftIO $ tryIOError io either (throwError . singleError . f) return e -- | Read a text file in the 'Make' monad, capturing any errors using the -- 'MonadError' instance. readTextFile :: FilePath -> Make B.ByteString readTextFile path = makeIO (const (ErrorMessage [] $ CannotReadFile path)) $ B.readFile path