2015-12-17 03:42:22 +00:00
|
|
|
{-# OPTIONS_HADDOCK ignore-exports #-}
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |This module provides all the atomic IO related file operations like
|
|
|
|
-- copy, delete, move and so on. It operates only on FilePaths and reads
|
|
|
|
-- all necessary file information manually in order to stay atomic and not
|
|
|
|
-- rely on the state of passed objects.
|
|
|
|
--
|
|
|
|
-- It would be nicer to pass states around, but the filesystem state changes
|
|
|
|
-- too quickly and cannot be relied upon. Lazy implementations of filesystem
|
|
|
|
-- trees have been tried as well, but they can introduce subtle bugs.
|
2015-12-17 15:25:37 +00:00
|
|
|
module IO.File where
|
2015-12-17 03:42:22 +00:00
|
|
|
|
|
|
|
|
2015-12-21 17:32:53 +00:00
|
|
|
import Control.Applicative
|
|
|
|
(
|
|
|
|
(<$>)
|
|
|
|
)
|
2015-12-17 03:42:22 +00:00
|
|
|
import Control.Exception
|
|
|
|
(
|
|
|
|
throw
|
|
|
|
)
|
|
|
|
import Control.Monad
|
|
|
|
(
|
|
|
|
unless
|
|
|
|
, void
|
|
|
|
)
|
2015-12-17 22:08:02 +00:00
|
|
|
import Data.DirTree
|
2015-12-18 03:22:13 +00:00
|
|
|
import Data.Foldable
|
|
|
|
(
|
|
|
|
for_
|
|
|
|
)
|
2015-12-17 03:42:22 +00:00
|
|
|
import IO.Error
|
2015-12-18 14:28:56 +00:00
|
|
|
import IO.Utils
|
2015-12-17 03:42:22 +00:00
|
|
|
import System.Directory
|
|
|
|
(
|
2015-12-17 22:08:02 +00:00
|
|
|
canonicalizePath
|
2015-12-18 14:28:56 +00:00
|
|
|
, createDirectory
|
2015-12-18 03:22:13 +00:00
|
|
|
, createDirectoryIfMissing
|
2015-12-17 22:08:02 +00:00
|
|
|
, doesDirectoryExist
|
2015-12-17 15:25:37 +00:00
|
|
|
, doesFileExist
|
2015-12-17 03:42:22 +00:00
|
|
|
, executable
|
2015-12-17 15:25:37 +00:00
|
|
|
, removeDirectory
|
2015-12-18 14:28:56 +00:00
|
|
|
, removeDirectoryRecursive
|
2015-12-17 15:25:37 +00:00
|
|
|
, removeFile
|
2015-12-17 03:42:22 +00:00
|
|
|
)
|
2015-12-17 16:46:55 +00:00
|
|
|
import System.FilePath
|
2015-12-17 03:42:22 +00:00
|
|
|
(
|
2015-12-17 22:08:02 +00:00
|
|
|
equalFilePath
|
|
|
|
, isAbsolute
|
|
|
|
, takeFileName
|
|
|
|
, takeDirectory
|
|
|
|
, (</>)
|
2015-12-17 03:42:22 +00:00
|
|
|
)
|
2015-12-18 03:22:13 +00:00
|
|
|
import System.Posix.Files
|
|
|
|
(
|
|
|
|
createSymbolicLink
|
|
|
|
, readSymbolicLink
|
2015-12-19 15:13:48 +00:00
|
|
|
, fileAccess
|
|
|
|
, getFileStatus
|
2015-12-18 03:22:13 +00:00
|
|
|
)
|
2015-12-17 03:42:22 +00:00
|
|
|
import System.Process
|
|
|
|
(
|
|
|
|
spawnProcess
|
|
|
|
, ProcessHandle
|
|
|
|
)
|
|
|
|
|
2015-12-17 22:08:02 +00:00
|
|
|
import qualified System.Directory as SD
|
2015-12-17 03:42:22 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
import qualified System.Posix.Files as PF
|
|
|
|
|
2015-12-17 22:08:02 +00:00
|
|
|
|
2015-12-17 22:11:18 +00:00
|
|
|
-- TODO: file operations should be threaded and not block the UI
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Data type describing an actual file operation that can be
|
|
|
|
-- carried out via `doFile`. Useful to build up a list of operations
|
|
|
|
-- or delay operations.
|
2015-12-18 15:55:46 +00:00
|
|
|
data FileOperation = FCopy Copy
|
|
|
|
| FMove Move
|
2015-12-19 15:13:48 +00:00
|
|
|
| FDelete FilePath
|
|
|
|
| FOpen FilePath
|
|
|
|
| FExecute FilePath [String]
|
2015-12-18 03:24:47 +00:00
|
|
|
| None
|
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Data type describing partial or complete file copy operation.
|
|
|
|
-- CC stands for a complete operation and can be used for `runFileOp`.
|
|
|
|
data Copy = CP1 FilePath
|
|
|
|
| CP2 FilePath FilePath
|
|
|
|
| CC FilePath FilePath DirCopyMode
|
2015-12-18 15:55:46 +00:00
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Data type describing partial or complete file move operation.
|
|
|
|
-- MC stands for a complete operation and can be used for `runFileOp`.
|
|
|
|
data Move = MP1 FilePath
|
|
|
|
| MC FilePath FilePath
|
2015-12-18 15:55:46 +00:00
|
|
|
|
|
|
|
|
2015-12-18 14:50:33 +00:00
|
|
|
-- |Directory copy modes.
|
2015-12-19 15:13:48 +00:00
|
|
|
data DirCopyMode = Strict -- ^ fail if the target directory already exists
|
|
|
|
| Merge -- ^ overwrite files if necessary
|
|
|
|
| Replace -- ^ remove target directory before copying
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Run a given FileOperation. If the FileOperation is partial, it will
|
|
|
|
-- be returned.
|
|
|
|
--
|
|
|
|
-- The operation may fail with:
|
|
|
|
--
|
|
|
|
-- * anything that `copyFileToDir`, `easyDelete`, `openFile`,
|
|
|
|
-- `executeFile` throws
|
2015-12-18 15:55:46 +00:00
|
|
|
runFileOp :: FileOperation -> IO (Maybe FileOperation)
|
2015-12-19 15:13:48 +00:00
|
|
|
runFileOp (FCopy (CC from to cm)) = easyCopy cm from to >> return Nothing
|
|
|
|
runFileOp (FCopy fo) = return $ Just $ FCopy fo
|
2015-12-18 15:55:46 +00:00
|
|
|
runFileOp (FDelete fp) = easyDelete fp >> return Nothing
|
|
|
|
runFileOp (FOpen fp) = openFile fp >> return Nothing
|
|
|
|
runFileOp (FExecute fp args) = executeFile fp args >> return Nothing
|
|
|
|
runFileOp _ = return Nothing
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
2015-12-18 14:28:56 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
--------------------
|
|
|
|
--[ File Copying ]--
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
2015-12-18 13:21:57 +00:00
|
|
|
-- TODO: allow renaming
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Copies a directory to the given destination with the specified
|
2015-12-21 17:32:53 +00:00
|
|
|
-- `DirCopyMode`. This is safe to call if the source directory is a symlink
|
|
|
|
-- in which case it will just be recreated.
|
2015-12-19 15:13:48 +00:00
|
|
|
--
|
|
|
|
-- The operation may fail with:
|
|
|
|
--
|
|
|
|
-- * `DirDoesNotExist` if the source or destination directory does not exist
|
|
|
|
-- * `DestinationInSource` if the destination directory is contained within
|
|
|
|
-- the source directory
|
|
|
|
-- * `SameFile` if the source and destination directory are the same
|
|
|
|
-- * `DirDoesExist` if the target directory already exists during the Strict
|
|
|
|
-- copy mode
|
|
|
|
-- * anything that `copyFileToDir`, `getFileStatus`, `createDirectory`,
|
|
|
|
-- `easyDelete`, `readSymbolicLink`, `createDirectoryIfMissing`,
|
|
|
|
-- `removeDirectoryRecursive`, `createSymbolicLink`, `copyDir`,
|
|
|
|
-- `copyFileToDir`, `getDirectoryContents` throws
|
2015-12-18 14:28:56 +00:00
|
|
|
copyDir :: DirCopyMode
|
2015-12-19 15:13:48 +00:00
|
|
|
-> FilePath -- ^ source dir
|
|
|
|
-> FilePath -- ^ destination dir
|
2015-12-18 03:22:13 +00:00
|
|
|
-> IO ()
|
2015-12-21 04:41:12 +00:00
|
|
|
copyDir cm from' to' = do
|
2015-12-21 16:15:31 +00:00
|
|
|
from <- canonicalizePath' from'
|
|
|
|
to <- canonicalizePath' to'
|
2015-12-21 17:32:53 +00:00
|
|
|
onSymlinkOr from (copyFileToDir from to) (go from to)
|
2015-12-18 14:42:24 +00:00
|
|
|
where
|
2015-12-21 04:41:12 +00:00
|
|
|
go from to = do
|
|
|
|
let fn = takeFileName from
|
|
|
|
destdir = to </> fn
|
|
|
|
|
|
|
|
dirSanityThrow from
|
|
|
|
dirSanityThrow to
|
|
|
|
throwDestinationInSource from to
|
|
|
|
throwSameFile from destdir
|
|
|
|
|
|
|
|
createDestdir destdir
|
|
|
|
|
|
|
|
contents <- getDirsFiles from
|
|
|
|
|
|
|
|
for_ contents $ \f -> do
|
|
|
|
let ffn = from </> f
|
|
|
|
fs <- PF.getSymbolicLinkStatus ffn
|
|
|
|
case (PF.isSymbolicLink fs, PF.isDirectory fs) of
|
2015-12-21 17:32:53 +00:00
|
|
|
(True, _) -> recreateSymlink' destdir f ffn
|
2015-12-21 04:41:12 +00:00
|
|
|
(_, True) -> copyDir cm ffn destdir
|
|
|
|
(_, _) -> copyFileToDir ffn destdir
|
2015-12-18 14:42:24 +00:00
|
|
|
createDestdir destdir =
|
|
|
|
case cm of
|
|
|
|
Merge ->
|
|
|
|
createDirectoryIfMissing False destdir
|
|
|
|
Strict -> do
|
|
|
|
throwDirDoesExist destdir
|
|
|
|
createDirectory destdir
|
|
|
|
Replace -> do
|
|
|
|
whenM (doesDirectoryExist destdir) (removeDirectoryRecursive destdir)
|
|
|
|
createDirectory destdir
|
2015-12-21 17:32:53 +00:00
|
|
|
recreateSymlink' destdir n f = do
|
2015-12-19 15:13:48 +00:00
|
|
|
let sympoint = destdir </> n
|
2015-12-18 14:42:24 +00:00
|
|
|
|
|
|
|
case cm of
|
2015-12-19 15:13:48 +00:00
|
|
|
-- delete old file/dir to be able to create symlink
|
|
|
|
Merge -> easyDelete sympoint
|
2015-12-18 14:42:24 +00:00
|
|
|
_ -> return ()
|
|
|
|
|
2015-12-21 17:32:53 +00:00
|
|
|
recreateSymlink f sympoint
|
|
|
|
|
|
|
|
|
2015-12-18 14:28:56 +00:00
|
|
|
|
2015-12-21 17:36:45 +00:00
|
|
|
-- |Recreate a symlink.
|
2015-12-21 17:32:53 +00:00
|
|
|
recreateSymlink :: FilePath -- ^ the old symlink file
|
|
|
|
-> FilePath -- ^ destination of the new symlink file
|
|
|
|
-> IO ()
|
2015-12-21 17:36:45 +00:00
|
|
|
recreateSymlink symf' symdest' = do
|
2015-12-21 17:32:53 +00:00
|
|
|
symf <- canonicalizePath' symf'
|
|
|
|
symname <- readSymbolicLink symf
|
2015-12-21 17:36:45 +00:00
|
|
|
symdestd <- canonicalizePath' (baseDir symdest')
|
|
|
|
let symdest = symdestd </> takeFileName symdest'
|
2015-12-21 17:32:53 +00:00
|
|
|
createSymbolicLink symname symdest
|
2015-12-18 03:22:13 +00:00
|
|
|
|
2015-12-21 17:32:53 +00:00
|
|
|
|
|
|
|
-- |Copies the given file. This can also be called on symlinks.
|
2015-12-17 22:08:02 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- The operation may fail with:
|
2015-12-17 22:08:02 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- * `PathNotAbsolute` either the source or destination file is not an
|
|
|
|
-- absolute path
|
|
|
|
-- * `FileDoesNotExist` the source file does not exist
|
|
|
|
-- * `DirDoesNotExist` the target directory does not exist
|
|
|
|
-- * `PathNotAbsolute` if either of the filepaths are not absolute
|
|
|
|
-- * `SameFile` if the source and destination files are the same
|
|
|
|
-- * anything that `canonicalizePath` or `System.Directory.copyFile` throws
|
|
|
|
copyFile :: FilePath -- ^ source file
|
|
|
|
-> FilePath -- ^ destination file
|
2015-12-17 22:08:02 +00:00
|
|
|
-> IO ()
|
2015-12-21 04:41:12 +00:00
|
|
|
copyFile from' to' = do
|
2015-12-21 16:15:31 +00:00
|
|
|
from <- canonicalizePath' from'
|
|
|
|
tod <- canonicalizePath' (baseDir to')
|
2015-12-21 04:50:19 +00:00
|
|
|
let to = tod </> takeFileName to'
|
2015-12-21 17:32:53 +00:00
|
|
|
onSymlinkOr from (recreateSymlink from to) $ do
|
|
|
|
fileSanityThrow from
|
|
|
|
throwNotAbsolute to
|
|
|
|
throwDirDoesExist to
|
|
|
|
toC <- canonicalizePath' (takeDirectory to)
|
|
|
|
let to' = toC </> takeFileName to
|
|
|
|
throwSameFile from to'
|
|
|
|
SD.copyFile from to'
|
2015-12-19 15:13:48 +00:00
|
|
|
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Copies the given file to the given dir with the same filename.
|
2015-12-21 17:32:53 +00:00
|
|
|
-- This can also be called on symlinks.
|
2015-12-17 22:08:02 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- The operation may fail with:
|
|
|
|
--
|
|
|
|
-- * `DirDoesNotExist` if the target directory does not exist
|
|
|
|
-- * `PathNotAbsolute` if the target directory is not absolute
|
|
|
|
-- * anything that `copyFile` throws
|
|
|
|
copyFileToDir :: FilePath -> FilePath -> IO ()
|
2015-12-21 04:41:12 +00:00
|
|
|
copyFileToDir from' to' = do
|
2015-12-21 16:15:31 +00:00
|
|
|
from <- canonicalizePath' from'
|
|
|
|
to <- canonicalizePath' to'
|
2015-12-19 15:13:48 +00:00
|
|
|
let name = takeFileName from
|
|
|
|
dirSanityThrow to
|
|
|
|
copyFile from (to </> name)
|
|
|
|
|
|
|
|
|
|
|
|
easyCopy :: DirCopyMode -> FilePath -> FilePath -> IO ()
|
2015-12-21 17:32:53 +00:00
|
|
|
easyCopy cm from to = onDirOrFile from (copyDir cm from to)
|
2015-12-19 15:13:48 +00:00
|
|
|
(copyFileToDir from to)
|
|
|
|
|
|
|
|
|
2015-12-17 15:25:37 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
---------------------
|
|
|
|
--[ File Deletion ]--
|
|
|
|
---------------------
|
2015-12-17 15:25:37 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
|
2015-12-21 17:32:53 +00:00
|
|
|
-- |Deletes a symlink, which can either point to a file or directory.
|
|
|
|
--
|
|
|
|
-- The operation may fail with:
|
|
|
|
--
|
|
|
|
-- * `dirSanityThrow`
|
|
|
|
-- * `fileSanityThrow`
|
|
|
|
deleteSymlink :: FilePath -> IO ()
|
|
|
|
deleteSymlink fp' = do
|
|
|
|
fp <- canonicalizePath' fp'
|
|
|
|
onDirOrFile fp (dirSanityThrow fp >> removeFile fp)
|
|
|
|
(fileSanityThrow fp >> removeFile fp)
|
|
|
|
|
|
|
|
|
|
|
|
-- |Deletes the given file.
|
2015-12-17 15:25:37 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- The operation may fail with:
|
2015-12-17 15:25:37 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- * `FileDoesNotExist` if the file does not exist
|
|
|
|
-- * `PathNotAbsolute` if the file is not absolute
|
|
|
|
-- * anything that `removeFile` throws
|
|
|
|
deleteFile :: FilePath -> IO ()
|
2015-12-21 04:41:12 +00:00
|
|
|
deleteFile fp' = do
|
2015-12-21 16:15:31 +00:00
|
|
|
fp <- canonicalizePath' fp'
|
2015-12-17 15:25:37 +00:00
|
|
|
fileSanityThrow fp
|
2015-12-21 17:32:53 +00:00
|
|
|
throwIsSymlink fp
|
2015-12-17 15:25:37 +00:00
|
|
|
removeFile fp
|
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Deletes the given directory.
|
2015-12-17 15:25:37 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- The operation may fail with:
|
2015-12-17 15:25:37 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- * `DirDoesNotExist` if the dir does not exist
|
|
|
|
-- * `PathNotAbsolute` if the dir is not absolute
|
|
|
|
-- * anything that `removeDirectory` throws
|
|
|
|
deleteDir :: FilePath -> IO ()
|
2015-12-21 17:32:53 +00:00
|
|
|
deleteDir fp' =
|
|
|
|
onSymlinkOr fp' (deleteFile fp') $ do
|
|
|
|
fp <- canonicalizePath' fp'
|
|
|
|
dirSanityThrow fp
|
|
|
|
throwIsSymlink fp
|
|
|
|
removeDirectory fp
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Deletes the given directory recursively.
|
2015-12-18 14:28:56 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- The operation may fail with:
|
2015-12-18 14:28:56 +00:00
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- * `DirDoesNotExist` if the dir does not exist
|
|
|
|
-- * `PathNotAbsolute` if the dir is not absolute
|
|
|
|
-- * anything that `removeDirectoryRecursive` throws
|
|
|
|
deleteDirRecursive :: FilePath -> IO ()
|
2015-12-21 17:32:53 +00:00
|
|
|
deleteDirRecursive fp' =
|
|
|
|
onSymlinkOr fp' (deleteFile fp') $ do
|
|
|
|
fp <- canonicalizePath' fp'
|
|
|
|
dirSanityThrow fp
|
|
|
|
throwIsSymlink fp
|
|
|
|
removeDirectoryRecursive fp
|
2015-12-18 14:28:56 +00:00
|
|
|
|
|
|
|
|
2015-12-21 17:32:53 +00:00
|
|
|
-- |Deletes a file, directory or symlink, whatever it may be.
|
2015-12-19 15:13:48 +00:00
|
|
|
--
|
|
|
|
-- The operation may fail with:
|
|
|
|
--
|
|
|
|
-- * `DirDoesNotExist`/`FileDoesNotExist` if the file/dir does not exist
|
|
|
|
-- * `PathNotAbsolute` if the file/dir is not absolute
|
|
|
|
-- * anything that `deleteDir`/`deleteFile` throws
|
|
|
|
easyDelete :: FilePath -> IO ()
|
2015-12-21 04:41:12 +00:00
|
|
|
easyDelete fp' = do
|
2015-12-21 16:15:31 +00:00
|
|
|
fp <- canonicalizePath' fp'
|
2015-12-21 17:32:53 +00:00
|
|
|
onSymlinkOr fp (deleteSymlink fp) $
|
|
|
|
onDirOrFile fp (deleteDir fp) (deleteFile fp)
|
2015-12-19 15:13:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--------------------
|
|
|
|
--[ File Opening ]--
|
|
|
|
--------------------
|
2015-12-17 15:25:37 +00:00
|
|
|
|
|
|
|
|
2015-12-17 03:42:22 +00:00
|
|
|
-- |Opens a file appropriately by invoking xdg-open.
|
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- The operation may fail with:
|
|
|
|
--
|
|
|
|
-- * `FileDoesNotExist` if the file does not exist
|
|
|
|
-- * `PathNotAbsolute` if the file is not absolute
|
|
|
|
openFile :: FilePath
|
2015-12-17 03:42:22 +00:00
|
|
|
-> IO ProcessHandle
|
2015-12-21 04:41:12 +00:00
|
|
|
openFile fp' = do
|
2015-12-21 16:15:31 +00:00
|
|
|
fp <- canonicalizePath' fp'
|
2015-12-17 03:42:22 +00:00
|
|
|
fileSanityThrow fp
|
|
|
|
spawnProcess "xdg-open" [fp]
|
|
|
|
|
|
|
|
|
|
|
|
-- |Executes a program with the given arguments.
|
|
|
|
--
|
2015-12-19 15:13:48 +00:00
|
|
|
-- The operation may fail with:
|
|
|
|
--
|
|
|
|
-- * `FileDoesNotExist` if the program does not exist
|
|
|
|
-- * `PathNotAbsolute` if the program is not absolute
|
|
|
|
-- * `FileNotExecutable` if the program is not executable
|
|
|
|
executeFile :: FilePath -- ^ program
|
|
|
|
-> [String] -- ^ arguments
|
2015-12-17 03:42:22 +00:00
|
|
|
-> IO ProcessHandle
|
2015-12-21 04:41:12 +00:00
|
|
|
executeFile prog' args = do
|
2015-12-21 16:15:31 +00:00
|
|
|
prog <- canonicalizePath' prog'
|
2015-12-19 15:13:48 +00:00
|
|
|
fileSanityThrow prog
|
|
|
|
unlessM (fileAccess prog False False True) (throw $ FileNotExecutable prog)
|
|
|
|
spawnProcess prog args
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-17 03:42:22 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
--------------------
|
|
|
|
--[ Utilities ]--
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
2015-12-21 17:39:30 +00:00
|
|
|
-- |Carries out the given action if the filepath is a symlink. If not,
|
|
|
|
-- carries out an alternative action.
|
2015-12-21 17:32:53 +00:00
|
|
|
onSymlinkOr :: FilePath
|
2015-12-21 17:39:30 +00:00
|
|
|
-> IO () -- ^ action if symlink
|
|
|
|
-> IO () -- ^ action if not symlink
|
2015-12-21 17:32:53 +00:00
|
|
|
-> IO ()
|
|
|
|
onSymlinkOr fp a1 a2 = do
|
|
|
|
isSymlink <- PF.isSymbolicLink <$> PF.getSymbolicLinkStatus fp
|
|
|
|
if isSymlink then a1 else a2
|
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Executes either a directory or file related IO action, depending on
|
|
|
|
-- the input filepath.
|
|
|
|
--
|
|
|
|
-- The operation may fail with:
|
|
|
|
--
|
|
|
|
-- * `throwFileDoesNotExist` if the filepath is neither a file or directory
|
2015-12-21 17:39:30 +00:00
|
|
|
onDirOrFile :: FilePath
|
|
|
|
-> IO () -- ^ action if directory
|
|
|
|
-> IO () -- ^ action if file
|
|
|
|
-> IO ()
|
2015-12-21 17:32:53 +00:00
|
|
|
onDirOrFile fp' iod iof = do
|
2015-12-21 16:15:31 +00:00
|
|
|
fp <- canonicalizePath' fp'
|
2015-12-19 15:13:48 +00:00
|
|
|
isD <- doesDirectoryExist fp
|
|
|
|
isF <- doesFileExist fp
|
|
|
|
case (isD, isF) of
|
|
|
|
(True, False) -> do
|
|
|
|
dirSanityThrow fp
|
|
|
|
iod
|
|
|
|
(False, True) -> do
|
|
|
|
fileSanityThrow fp
|
|
|
|
iof
|
|
|
|
_ -> throwFileDoesNotExist fp
|