{-# 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