directory-layout-0.1.0.0: src/System/Directory/Layout/Make.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UnicodeSyntax #-}
-- | Make layout as specified
--
-- For example, suppose you are in an empty directory
--
-- @
-- % tree
-- .
-- @
--
-- and you've written simple layout:
--
-- @
-- layout = do
-- directory \"baz\" $
-- file_ \"twey\"
-- directory \"foo\" $ do
-- directory \"bar\" $ do
-- file_ \"quuz\"
-- file_ \"tatata\"
-- file_ \"quux\"
-- @
--
-- then running it should result in this directory tree:
--
-- @
-- % tree
-- .
-- ├── baz
-- │ └── twey
-- └── foo
-- ├── bar
-- │ ├── quuz
-- │ └── tatata
-- └── quux
-- @
--
module System.Directory.Layout.Make
( DLMakeWarning(..), make
) where
import Control.Arrow (second)
import Control.Monad.Trans.Reader (ReaderT, runReaderT, ask, local)
import Control.Monad.Trans.Writer (WriterT, execWriterT, tell)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Class (lift)
import Data.Text (Text)
import qualified Data.Text.IO as T
import System.FilePath ((</>), makeRelative)
import System.Directory
import System.Directory.Layout.Internal
-- | Infect file layout with stuff from script
make ∷ Layout
→ FilePath -- ^ Root directory
→ IO [DLMakeWarning] -- ^ List of warnings
make z fp = do
d ← getCurrentDirectory
fp' ← canonicalizePath fp
setCurrentDirectory fp'
xs ← runRunT (fp', fp') (f z)
setCurrentDirectory d
return xs
where
f (E _) = return ()
f (F p Nothing x) = touchFile p >> f x
f (F p (Just c) x) = touchFile p >> infectFile p c >> f x
f (D p x y) = createDir p >> changeDir p (f x) >> f y
-- | Data type representing various warnings
-- that may occur while infecting directory layout
data DLMakeWarning =
FileDoesExist FilePath
| DirectoryDoesExist FilePath
deriving (Show, Read, Eq, Ord)
type RunT = ReaderT (FilePath, FilePath) (WriterT [DLMakeWarning] IO)
runRunT ∷ (FilePath, FilePath) → RunT a → IO [DLMakeWarning]
runRunT e = execWriterT . flip runReaderT e
-- | File creation
-- emits 'FileDoesExist' if file exists already
touchFile ∷ FilePath → RunT ()
touchFile p = do
(r, d) ← ask
z ← io $ doesFileExist (d </> p)
if z
then tell' [FileDoesExist (makeRelative r d </> p)]
else io $ T.writeFile (d </> p) ""
infectFile ∷ FilePath → Text → RunT ()
infectFile p c = do
(_, d) ← ask
io $ T.writeFile (d </> p) c
-- | Directory creation
-- emits 'DirectoryDoesExist' if directory exists already
createDir ∷ FilePath → RunT ()
createDir p = do
(r, d) ← ask
z ← io $ doesDirectoryExist (d </> p)
if z
then tell' [DirectoryDoesExist (makeRelative r d </> p)]
else io $ createDirectory (d </> p)
changeDir ∷ FilePath → RunT () → RunT ()
changeDir fp = local (second (</> fp))
io ∷ IO a → RunT a
io = liftIO
tell' ∷ [DLMakeWarning] → RunT ()
tell' = lift . tell