content: module Pier.Core.Internal.Directory
      ( forFileRecursive_
      , FileItem(..)
      , getRegularContents
      , createParentIfMissing
      , copyDirectory
      , parentDirectory
      ) where
  
  import Control.Monad.IO.Class
  import Development.Shake.FilePath
  import System.Directory
  import qualified System.Posix.Files as Posix
  
  -- | Create recursively the parent of the given path, if it doesn't exist.
  createParentIfMissing :: MonadIO m => FilePath -> m ()
  createParentIfMissing
      = liftIO . createDirectoryIfMissing True . parentDirectory
  
  -- | Get the parent of the given directory or file.
  --
  -- Examples:
  --
  -- parentDirectory "foo/bar"  == "foo"
  -- parentDirectory "foo/bar/" == "foo"
  -- parentDirectory "foo" == ""
  parentDirectory :: FilePath -> FilePath
  parentDirectory = fixPeriod . takeDirectory . dropTrailingPathSeparator
    where
      fixPeriod "." = ""
      fixPeriod x = x
  
  data FileItem = RegularFile | DirectoryStart | DirectoryEnd | SymbolicLink
  
  forFileRecursive_ :: (FileItem -> FilePath -> IO ()) -> FilePath -> IO ()
  forFileRecursive_ act f = do
      isSymLink <- pathIsSymbolicLink f
      if isSymLink
          then act SymbolicLink f
          else do
              isDir <- doesDirectoryExist f
              if not isDir
                  then act RegularFile f
                  else do
                      act DirectoryStart f
                      getRegularContents f
                          >>= mapM_ (forFileRecursive_ act . (f </>))
                      act DirectoryEnd f
  
  -- | Get the contents of this path, excluding the special files "." and ".."
  getRegularContents :: FilePath -> IO [FilePath]
  getRegularContents f =
      filter (not . specialFile) <$> getDirectoryContents f
    where
      specialFile "." = True
      specialFile ".." = True
      specialFile _ = False
  
  -- | Copy the directory recursively from the source to the target location.
  -- Hard-link files, and copy any symlinks.
  copyDirectory :: FilePath -> FilePath -> IO ()
  copyDirectory src dest = do
      createParentIfMissing dest
      forFileRecursive_ act src
    where
      act RegularFile f = Posix.createLink f $ dest </> makeRelative src f
      act SymbolicLink f = do
          target <- getSymbolicLinkTarget f
          let g = dest </> makeRelative src f
          createParentIfMissing g
          createFileLink target g
      act DirectoryStart f = createDirectoryIfMissing False (dest </> makeRelative src f)
      act DirectoryEnd _ = return ()
  