axel-0.0.13: src/Axel/Eff/FileSystem.hs
{-# LANGUAGE TemplateHaskell #-}
module Axel.Eff.FileSystem where
import Axel.Prelude
import Axel.Utils.FilePath ((</>))
import Control.Monad (forM)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Effectful ((:>))
import qualified Effectful as Eff
import qualified Effectful.Dispatch.Dynamic as Eff
import qualified Effectful.TH as Eff
import qualified System.Directory
( copyFile
, createDirectoryIfMissing
, doesDirectoryExist
, getCurrentDirectory
, getDirectoryContents
, getTemporaryDirectory
, removeFile
, setCurrentDirectory
)
data FileSystem :: Eff.Effect where
AppendFile :: FilePath -> Text -> FileSystem m ()
CopyFile :: FilePath -> FilePath -> FileSystem m ()
CreateDirectoryIfMissing :: Bool -> FilePath -> FileSystem m ()
DoesDirectoryExist :: FilePath -> FileSystem m Bool
GetCurrentDirectory :: FileSystem m FilePath
GetDirectoryContents :: FilePath -> FileSystem m [FilePath]
GetTemporaryDirectory :: FileSystem m FilePath
ReadFile :: FilePath -> FileSystem m Text
RemoveFile :: FilePath -> FileSystem m ()
SetCurrentDirectory :: FilePath -> FileSystem m ()
WriteFile :: FilePath -> Text -> FileSystem m ()
Eff.makeEffect ''FileSystem
runFileSystem ::
(Eff.IOE :> effs) => Eff.Eff (FileSystem ': effs) a -> Eff.Eff effs a
runFileSystem =
Eff.interpret $ \_ ->
\case
AppendFile (FilePath path) contents ->
Eff.liftIO $ T.appendFile (T.unpack path) contents
CopyFile (FilePath src) (FilePath dest) ->
Eff.liftIO $ System.Directory.copyFile (T.unpack src) (T.unpack dest)
CreateDirectoryIfMissing createParentDirs (FilePath path) ->
Eff.liftIO $
System.Directory.createDirectoryIfMissing
createParentDirs
(T.unpack path)
DoesDirectoryExist (FilePath path) ->
Eff.liftIO $ System.Directory.doesDirectoryExist (T.unpack path)
GetCurrentDirectory ->
Eff.liftIO $ FilePath . T.pack <$> System.Directory.getCurrentDirectory
GetDirectoryContents (FilePath path) ->
Eff.liftIO $
map (FilePath . T.pack) <$>
System.Directory.getDirectoryContents (T.unpack path)
GetTemporaryDirectory ->
Eff.liftIO $
FilePath . T.pack <$> System.Directory.getTemporaryDirectory
ReadFile (FilePath path) -> Eff.liftIO $ T.readFile (T.unpack path)
RemoveFile (FilePath path) ->
Eff.liftIO $ System.Directory.removeFile (T.unpack path)
SetCurrentDirectory (FilePath path) ->
Eff.liftIO $ System.Directory.setCurrentDirectory (T.unpack path)
WriteFile (FilePath path) contents ->
Eff.liftIO $ T.writeFile (T.unpack path) contents
-- Adapted from http://book.realworldhaskell.org/read/io-case-study-a-library-for-searching-the-filesystem.html.
getDirectoryContentsRec ::
(FileSystem :> effs) => FilePath -> Eff.Eff effs [FilePath]
getDirectoryContentsRec dir = do
names <- getDirectoryContents dir
let properNames =
filter (\(FilePath path) -> path `notElem` [".", ".."]) names
paths <-
forM properNames $ \name -> do
let path = dir </> name
isDirectory <- doesDirectoryExist path
if isDirectory
then getDirectoryContentsRec path
else pure [path]
pure $ concat paths
withCurrentDirectory ::
(FileSystem :> effs) => FilePath -> Eff.Eff effs a -> Eff.Eff effs a
withCurrentDirectory directory f = do
originalDirectory <- getCurrentDirectory
setCurrentDirectory directory
result <- f
setCurrentDirectory originalDirectory
pure result
withTemporaryDirectory ::
(FileSystem :> effs) => (FilePath -> Eff.Eff effs a) -> Eff.Eff effs a
withTemporaryDirectory action = getTemporaryDirectory >>= action