packages feed

hsinstall-2.5: src/app/HSInstall/System/Directory.hs

{-# LANGUAGE OverloadedStrings #-}

module HSInstall.System.Directory
  ( copyTree
  )
  where

import Control.Monad ( when )
import Data.List ( (\\) )
import Fmt ( (+|), (|+), fmtLn )
import System.Directory ( copyFile, createDirectoryIfMissing,
  doesDirectoryExist, getDirectoryContents )
import System.FilePath ( (</>) )


{-
  Recursive file copying code

  It proved difficult to find a stock recursive file copy utility that
  preserved the executable bits on things.

  chatty in this context means describe the file copies on stdout.

  Many thanks to [abuzittin gillifirca](https://codereview.stackexchange.com/users/20251/abuzittin-gillifirca)
  for the StackOverflow post [Copying files in Haskell](https://codereview.stackexchange.com/questions/68908/copying-files-in-haskell)
  from which the following code was lifted.
-}

copyTree :: Bool -> FilePath -> FilePath -> IO ()
copyTree chatty s t = do
  createDirectoryIfMissing True t
  subItems <- getSubitems s
  mapM_ (copyItem chatty s t) subItems


getSubitems :: FilePath -> IO [(Bool, FilePath)]
getSubitems path = getSubitems' ""
  where
    getChildren path' =  (\\ [".", ".."]) <$> getDirectoryContents path'

    getSubitems' relPath = do
      let absPath = path </> relPath
      isDir <- doesDirectoryExist absPath
      children <- if isDir then getChildren absPath else return []
      let relChildren = [relPath </> p | p <- children]
      ((isDir, relPath) :) . concat <$> mapM getSubitems' relChildren


copyItem :: Bool -> FilePath -> FilePath -> (Bool, FilePath) -> IO ()
copyItem chatty baseSourcePath baseTargetPath (isDir, relativePath) = do
  let sourcePath = baseSourcePath </> relativePath
  let targetPath = baseTargetPath </> relativePath

  when chatty $
    fmtLn $ "Copying "+|sourcePath|+" to "+|targetPath|+""

  if isDir
    then createDirectoryIfMissing False targetPath
    else copyFile sourcePath targetPath