{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-overlapping-patterns #-} module Network.CloudSeeder.Monads.FileSystem ( MonadFileSystem(..) , FileSystemError(..) , readFile' , HasFileSystemError(..) , AsFileSystemError(..) ) where import Prelude hiding (readFile) import Control.Lens (to) import Control.Lens.TH (makeClassy, makeClassyPrisms) import Control.Monad.Base (MonadBase, liftBase) import Control.Monad.Error.Lens (throwing) import Control.Monad.Except (ExceptT, MonadError) import Control.Monad.Logger (LoggingT) import Control.Monad.Reader (ReaderT) import Control.Monad.State (StateT) import Control.Monad.Trans (MonadTrans, lift) import Control.Monad.Writer (WriterT) import GHC.IO.Exception (IOException(..), IOErrorType(..)) import qualified Control.Exception.Lens as IO import qualified Data.Text as T import qualified Data.Text.IO as T newtype FileSystemError = FileNotFound T.Text deriving (Eq, Show) makeClassy ''FileSystemError makeClassyPrisms ''FileSystemError -- | A class of monads that can interact with the filesystem. class (AsFileSystemError e, MonadError e m) => MonadFileSystem e m | m -> e where -- | Reads a file at the given path and returns its contents. If the file does -- not exist, is not accessible, or is improperly encoded, this method throws -- an exception. readFile :: T.Text -> m T.Text default readFile :: (MonadTrans t, MonadFileSystem e m', m ~ t m') => T.Text -> m T.Text readFile = lift . readFile readFile' :: (AsFileSystemError e, MonadError e m, MonadBase IO m) => T.Text -> m T.Text readFile' p = do let _IOException_NoSuchThing = IO._IOException . to isNoSuchThingIOError x <- liftBase $ IO.catching_ _IOException_NoSuchThing (Just <$> T.readFile (T.unpack p)) (return Nothing) maybe (throwing _FileNotFound p) return x where isNoSuchThingIOError IOError { ioe_type = NoSuchThing } = True isNoSuchThingIOError _ = False instance MonadFileSystem e m => MonadFileSystem e (ExceptT e m) instance MonadFileSystem e m => MonadFileSystem e (LoggingT m) instance MonadFileSystem e m => MonadFileSystem e (ReaderT r m) instance MonadFileSystem e m => MonadFileSystem e (StateT s m) instance (MonadFileSystem e m, Monoid w) => MonadFileSystem e (WriterT w m)