LIB/GTK: refactor File API and add copyFile
This commit is contained in:
parent
d5c6eef49e
commit
87ad7d02f0
@ -39,6 +39,8 @@ import qualified Data.List as DL
|
|||||||
-- are the breadcrumbs.
|
-- are the breadcrumbs.
|
||||||
type DTZipper a b = (DirTree a b, [DirTree a b])
|
type DTZipper a b = (DirTree a b, [DirTree a b])
|
||||||
|
|
||||||
|
type DTInfoZipper = DTZipper DirTreeInfo DirTreeInfo
|
||||||
|
|
||||||
|
|
||||||
-- |The base zipper of a tree with empty crumbs element.
|
-- |The base zipper of a tree with empty crumbs element.
|
||||||
baseZipper :: DirTree a b -> DTZipper a b
|
baseZipper :: DirTree a b -> DTZipper a b
|
||||||
|
@ -135,14 +135,15 @@ data FMSettings = MkFMSettings {
|
|||||||
-- because we might want to have multiple views.
|
-- because we might want to have multiple views.
|
||||||
data MyView = MkMyView {
|
data MyView = MkMyView {
|
||||||
-- |raw model with unsorted data
|
-- |raw model with unsorted data
|
||||||
rawModel :: TVar (ListStore (DTZipper DirTreeInfo DirTreeInfo))
|
rawModel :: TVar (ListStore DTInfoZipper)
|
||||||
-- |sorted proxy model
|
-- |sorted proxy model
|
||||||
, sortedModel :: TVar (TypedTreeModelSort
|
, sortedModel :: TVar (TypedTreeModelSort DTInfoZipper)
|
||||||
(DTZipper DirTreeInfo DirTreeInfo))
|
|
||||||
-- |filtered proxy model
|
-- |filtered proxy model
|
||||||
, filteredModel :: TVar (TypedTreeModelFilter
|
, filteredModel :: TVar (TypedTreeModelFilter DTInfoZipper)
|
||||||
(DTZipper DirTreeInfo DirTreeInfo))
|
, fsState :: TVar DTInfoZipper
|
||||||
, fsState :: TVar (DTZipper DirTreeInfo DirTreeInfo)
|
, operationBuffer :: TVar (Either
|
||||||
|
(DTInfoZipper -> FileOperation DirTreeInfo DirTreeInfo)
|
||||||
|
(FileOperation DirTreeInfo DirTreeInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -169,6 +170,14 @@ setCallbacks mygui myview = do
|
|||||||
_ <- treeView mygui `on` rowActivated $ (\_ _ -> withRow mygui myview open)
|
_ <- treeView mygui `on` rowActivated $ (\_ _ -> withRow mygui myview open)
|
||||||
_ <- menubarFileQuit mygui `on` menuItemActivated $ mainQuit
|
_ <- menubarFileQuit mygui `on` menuItemActivated $ mainQuit
|
||||||
_ <- urlBar mygui `on` entryActivated $ urlGoTo mygui myview
|
_ <- urlBar mygui `on` entryActivated $ urlGoTo mygui myview
|
||||||
|
_ <- treeView mygui `on` keyPressEvent $ tryEvent $ do
|
||||||
|
[Control] <- eventModifier
|
||||||
|
"c" <- fmap glibToString eventKeyName
|
||||||
|
liftIO $ withRow mygui myview copyInit
|
||||||
|
_ <- treeView mygui `on` keyPressEvent $ tryEvent $ do
|
||||||
|
[Control] <- eventModifier
|
||||||
|
"v" <- fmap glibToString eventKeyName
|
||||||
|
liftIO $ copyFinal mygui myview
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
|
||||||
@ -188,7 +197,7 @@ urlGoTo mygui myview = do
|
|||||||
-- |Gets the currently selected row of the treeView, if any.
|
-- |Gets the currently selected row of the treeView, if any.
|
||||||
getSelectedRow :: MyGUI
|
getSelectedRow :: MyGUI
|
||||||
-> MyView
|
-> MyView
|
||||||
-> IO (Maybe (DTZipper DirTreeInfo DirTreeInfo))
|
-> IO (Maybe DTInfoZipper)
|
||||||
getSelectedRow mygui myview = do
|
getSelectedRow mygui myview = do
|
||||||
(tp, _) <- treeViewGetCursor $ treeView mygui
|
(tp, _) <- treeViewGetCursor $ treeView mygui
|
||||||
rawModel' <- readTVarIO $ rawModel myview
|
rawModel' <- readTVarIO $ rawModel myview
|
||||||
@ -206,7 +215,7 @@ getSelectedRow mygui myview = do
|
|||||||
-- If there is no row selected, does nothing.
|
-- If there is no row selected, does nothing.
|
||||||
withRow :: MyGUI
|
withRow :: MyGUI
|
||||||
-> MyView
|
-> MyView
|
||||||
-> ( DTZipper DirTreeInfo DirTreeInfo
|
-> ( DTInfoZipper
|
||||||
-> MyGUI
|
-> MyGUI
|
||||||
-> MyView
|
-> MyView
|
||||||
-> IO ()) -- ^ action to carry out
|
-> IO ()) -- ^ action to carry out
|
||||||
@ -217,32 +226,65 @@ withRow mygui myview io = do
|
|||||||
|
|
||||||
|
|
||||||
-- |Supposed to be used with `withRow`. Opens a file or directory.
|
-- |Supposed to be used with `withRow`. Opens a file or directory.
|
||||||
open :: DTZipper DirTreeInfo DirTreeInfo -> MyGUI -> MyView -> IO ()
|
open :: DTInfoZipper -> MyGUI -> MyView -> IO ()
|
||||||
open row mygui myview = case row of
|
open row mygui myview = case row of
|
||||||
(Dir {}, _) ->
|
(Dir {}, _) ->
|
||||||
refreshTreeView' mygui myview row
|
refreshTreeView' mygui myview row
|
||||||
dz@(File {}, _) ->
|
dz@(File {}, _) ->
|
||||||
withErrorDialog $ openFile (getFullPath dz)
|
withErrorDialog $ openFile dz
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
|
||||||
|
|
||||||
-- |Supposed to be used with `withRow`. Deletes a file or directory.
|
-- |Supposed to be used with `withRow`. Deletes a file or directory.
|
||||||
del :: DTZipper DirTreeInfo DirTreeInfo -> MyGUI -> MyView -> IO ()
|
del :: DTInfoZipper -> MyGUI -> MyView -> IO ()
|
||||||
del row mygui myview = case row of
|
del row mygui myview = case row of
|
||||||
dz@(Dir {}, _) -> do
|
dz@(Dir {}, _) -> do
|
||||||
let fp = getFullPath dz
|
let fp = getFullPath dz
|
||||||
cmsg = "Really delete directory \"" ++ fp ++ "\"?"
|
cmsg = "Really delete directory \"" ++ fp ++ "\"?"
|
||||||
withConfirmationDialog cmsg
|
withConfirmationDialog cmsg
|
||||||
$ withErrorDialog (deleteDir fp
|
$ withErrorDialog (deleteDir dz
|
||||||
>> refreshTreeView mygui myview Nothing)
|
>> refreshTreeView mygui myview Nothing)
|
||||||
dz@(File {}, _) -> do
|
dz@(File {}, _) -> do
|
||||||
let fp = getFullPath dz
|
let fp = getFullPath dz
|
||||||
cmsg = "Really delete file \"" ++ fp ++ "\"?"
|
cmsg = "Really delete file \"" ++ fp ++ "\"?"
|
||||||
withConfirmationDialog cmsg
|
withConfirmationDialog cmsg
|
||||||
$ withErrorDialog (deleteFile fp
|
$ withErrorDialog (deleteFile dz
|
||||||
>> refreshTreeView mygui myview Nothing)
|
>> refreshTreeView mygui myview Nothing)
|
||||||
|
|
||||||
|
|
||||||
|
-- |Supposed to be used with `withRow`. Initializes a file copy operation.
|
||||||
|
copyInit :: DTInfoZipper -> MyGUI -> MyView -> IO ()
|
||||||
|
copyInit row mygui myview = case row of
|
||||||
|
dz@(File {}, _) -> do
|
||||||
|
print "blah1"
|
||||||
|
writeTVarIO (operationBuffer myview) (Left $ FCopy dz)
|
||||||
|
return ()
|
||||||
|
_ -> return ()
|
||||||
|
|
||||||
|
|
||||||
|
-- |Finalizes a file copy operation.
|
||||||
|
copyFinal :: MyGUI -> MyView -> IO ()
|
||||||
|
copyFinal mygui myview = do
|
||||||
|
mOp <- readTVarIO (operationBuffer myview)
|
||||||
|
op <- case mOp of
|
||||||
|
Left pOp -> do
|
||||||
|
curDir <- readTVarIO (fsState myview)
|
||||||
|
case pOp curDir of
|
||||||
|
op@(FCopy _ _) -> return op
|
||||||
|
_ -> return None
|
||||||
|
Right op@(FCopy _ _) -> return op
|
||||||
|
_ -> return None
|
||||||
|
doCopy op
|
||||||
|
where
|
||||||
|
doCopy op@(FCopy from to) = do
|
||||||
|
let cmsg = "Really copy file \"" ++ getFullPath from
|
||||||
|
++ "\"" ++ " to \"" ++ getFullPath to ++ "\"?"
|
||||||
|
withConfirmationDialog cmsg
|
||||||
|
$ withErrorDialog
|
||||||
|
(runFileOp op >> refreshTreeView mygui myview Nothing)
|
||||||
|
doCopy _ = return ()
|
||||||
|
|
||||||
|
|
||||||
-- |Go up one directory and visualize it in the treeView.
|
-- |Go up one directory and visualize it in the treeView.
|
||||||
upDir :: MyGUI -> MyView -> IO ()
|
upDir :: MyGUI -> MyView -> IO ()
|
||||||
upDir mygui myview = do
|
upDir mygui myview = do
|
||||||
@ -257,9 +299,9 @@ upDir mygui myview = do
|
|||||||
-- into the GTK+ data structures.
|
-- into the GTK+ data structures.
|
||||||
--
|
--
|
||||||
-- This also updates the TVar `fsState` inside the given view.
|
-- This also updates the TVar `fsState` inside the given view.
|
||||||
fileListStore :: DTZipper DirTreeInfo DirTreeInfo -- ^ current dir
|
fileListStore :: DTInfoZipper -- ^ current dir
|
||||||
-> MyView
|
-> MyView
|
||||||
-> IO (ListStore (DTZipper DirTreeInfo DirTreeInfo))
|
-> IO (ListStore DTInfoZipper)
|
||||||
fileListStore dtz myview = do
|
fileListStore dtz myview = do
|
||||||
writeTVarIO (fsState myview) dtz
|
writeTVarIO (fsState myview) dtz
|
||||||
listStoreNew (goAllDown dtz)
|
listStoreNew (goAllDown dtz)
|
||||||
@ -297,7 +339,7 @@ refreshTreeView mygui myview mfp = do
|
|||||||
-- This also updates the TVar `rawModel`.
|
-- This also updates the TVar `rawModel`.
|
||||||
refreshTreeView' :: MyGUI
|
refreshTreeView' :: MyGUI
|
||||||
-> MyView
|
-> MyView
|
||||||
-> DTZipper DirTreeInfo DirTreeInfo
|
-> DTInfoZipper
|
||||||
-> IO ()
|
-> IO ()
|
||||||
refreshTreeView' mygui myview dtz = do
|
refreshTreeView' mygui myview dtz = do
|
||||||
newRawModel <- fileListStore dtz myview
|
newRawModel <- fileListStore dtz myview
|
||||||
@ -433,6 +475,8 @@ startMainWindow = do
|
|||||||
|
|
||||||
fsState <- readPath' "/" >>= newTVarIO
|
fsState <- readPath' "/" >>= newTVarIO
|
||||||
|
|
||||||
|
operationBuffer <- newTVarIO (Right None)
|
||||||
|
|
||||||
builder <- builderNew
|
builder <- builderNew
|
||||||
builderAddFromFile builder "data/Gtk/builder.xml"
|
builderAddFromFile builder "data/Gtk/builder.xml"
|
||||||
|
|
||||||
|
@ -7,17 +7,75 @@ module IO.Error where
|
|||||||
import Control.Exception
|
import Control.Exception
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
(
|
(
|
||||||
mzero
|
unless
|
||||||
, MonadPlus
|
, void
|
||||||
|
, when
|
||||||
)
|
)
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
|
import System.Directory
|
||||||
|
(
|
||||||
|
doesDirectoryExist
|
||||||
|
, doesFileExist
|
||||||
|
)
|
||||||
|
import System.FilePath
|
||||||
|
(
|
||||||
|
equalFilePath
|
||||||
|
, isAbsolute
|
||||||
|
, takeFileName
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
data FmIOException = FileDoesNotExist String
|
data FmIOException = FileDoesNotExist String
|
||||||
| PathNotAbsolute String
|
| PathNotAbsolute String
|
||||||
| FileNotExecutable String
|
| FileNotExecutable String
|
||||||
|
| SameFile String String
|
||||||
|
| NotAFile String
|
||||||
|
| NotADir String
|
||||||
deriving (Show, Typeable)
|
deriving (Show, Typeable)
|
||||||
|
|
||||||
|
|
||||||
instance Exception FmIOException
|
instance Exception FmIOException
|
||||||
|
|
||||||
|
|
||||||
|
-- Throws an exception if the filepath is not absolute
|
||||||
|
-- or the file does not exist.
|
||||||
|
fileSanityThrow :: FilePath -> IO ()
|
||||||
|
fileSanityThrow fp = do
|
||||||
|
throwNotAbsolute fp
|
||||||
|
throwFileDoesNotExist fp
|
||||||
|
|
||||||
|
|
||||||
|
-- Throws an exception if the filepath is not absolute
|
||||||
|
-- or the dir does not exist.
|
||||||
|
dirSanityThrow :: FilePath -> IO ()
|
||||||
|
dirSanityThrow fp = do
|
||||||
|
throwNotAbsolute fp
|
||||||
|
throwDirDoesNotExist fp
|
||||||
|
|
||||||
|
|
||||||
|
throwNotAbsolute :: FilePath -> IO ()
|
||||||
|
throwNotAbsolute fp = unless (isAbsolute fp) (throw $ PathNotAbsolute fp)
|
||||||
|
|
||||||
|
|
||||||
|
throwDirDoesExist :: FilePath -> IO ()
|
||||||
|
throwDirDoesExist fp = do
|
||||||
|
exists <- doesDirectoryExist fp
|
||||||
|
when exists (throw $ FileDoesNotExist fp)
|
||||||
|
|
||||||
|
|
||||||
|
throwDirDoesNotExist :: FilePath -> IO ()
|
||||||
|
throwDirDoesNotExist fp = do
|
||||||
|
exists <- doesDirectoryExist fp
|
||||||
|
unless exists (throw $ FileDoesNotExist fp)
|
||||||
|
|
||||||
|
|
||||||
|
throwFileDoesNotExist :: FilePath -> IO ()
|
||||||
|
throwFileDoesNotExist fp = do
|
||||||
|
exists <- doesFileExist fp
|
||||||
|
unless exists (throw $ FileDoesNotExist fp)
|
||||||
|
|
||||||
|
|
||||||
|
throwSameFile :: FilePath -- ^ should be canonicalized
|
||||||
|
-> FilePath -- ^ should be canonicalized
|
||||||
|
-> IO ()
|
||||||
|
throwSameFile fp1 fp2 = when (equalFilePath fp1 fp2) (throw $ SameFile fp1 fp2)
|
||||||
|
129
src/IO/File.hs
129
src/IO/File.hs
@ -11,11 +11,15 @@ import Control.Monad
|
|||||||
(
|
(
|
||||||
unless
|
unless
|
||||||
, void
|
, void
|
||||||
|
, when
|
||||||
)
|
)
|
||||||
|
import Data.DirTree
|
||||||
|
import Data.DirTree.Zipper
|
||||||
import IO.Error
|
import IO.Error
|
||||||
import System.Directory
|
import System.Directory
|
||||||
(
|
(
|
||||||
doesDirectoryExist
|
canonicalizePath
|
||||||
|
, doesDirectoryExist
|
||||||
, doesFileExist
|
, doesFileExist
|
||||||
, executable
|
, executable
|
||||||
, getPermissions
|
, getPermissions
|
||||||
@ -24,7 +28,11 @@ import System.Directory
|
|||||||
)
|
)
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
(
|
(
|
||||||
isAbsolute
|
equalFilePath
|
||||||
|
, isAbsolute
|
||||||
|
, takeFileName
|
||||||
|
, takeDirectory
|
||||||
|
, (</>)
|
||||||
)
|
)
|
||||||
import System.Process
|
import System.Process
|
||||||
(
|
(
|
||||||
@ -32,12 +40,72 @@ import System.Process
|
|||||||
, ProcessHandle
|
, ProcessHandle
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import qualified System.Directory as SD
|
||||||
|
|
||||||
data FileOperation = Copy
|
|
||||||
| Move
|
-- TODO: modify the DTZipper directly after file operations!?
|
||||||
| Delete
|
|
||||||
| Open
|
|
||||||
| Execute
|
-- |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.
|
||||||
|
data FileOperation a b = FCopy (DTZipper a b) (DTZipper a b)
|
||||||
|
| FMove FilePath FilePath
|
||||||
|
| FDelete (DTZipper a b)
|
||||||
|
| FOpen (DTZipper a b)
|
||||||
|
| FExecute (DTZipper a b) [String]
|
||||||
|
| None
|
||||||
|
|
||||||
|
|
||||||
|
runFileOp :: FileOperation a b -> IO ()
|
||||||
|
runFileOp (FCopy from to) = copyFileToDir from to
|
||||||
|
runFileOp (FDelete fp) = easyDelete fp
|
||||||
|
runFileOp (FOpen fp) = void $ openFile fp
|
||||||
|
runFileOp (FExecute fp args) = void $ executeFile fp args
|
||||||
|
runFileOp _ = return ()
|
||||||
|
|
||||||
|
|
||||||
|
-- |Copies the given file.
|
||||||
|
--
|
||||||
|
-- This will throw an exception if any of the filepaths are not absolute
|
||||||
|
-- and an exception if the source file does not exist.
|
||||||
|
--
|
||||||
|
-- If the destination file already exists, it will be replaced.
|
||||||
|
-- TODO: don't permit copying file A to file A
|
||||||
|
copyFile :: DTZipper a b -- ^ source file
|
||||||
|
-> FilePath -- ^ destination file
|
||||||
|
-> IO ()
|
||||||
|
copyFile from@(File name _, _) to = do
|
||||||
|
let fp = getFullPath from
|
||||||
|
fileSanityThrow fp
|
||||||
|
throwNotAbsolute to
|
||||||
|
throwDirDoesExist to
|
||||||
|
toC <- canonicalizePath (takeDirectory to)
|
||||||
|
let to' = toC </> takeFileName to
|
||||||
|
throwSameFile fp to'
|
||||||
|
SD.copyFile fp to'
|
||||||
|
|
||||||
|
|
||||||
|
-- |Copies the given file to the given dir with the same filename.
|
||||||
|
--
|
||||||
|
-- This is just a convenience wrapper around `copyFile`.
|
||||||
|
copyFileToDir :: DTZipper a b -- ^ source file
|
||||||
|
-> DTZipper a b -- ^ destination
|
||||||
|
-> IO ()
|
||||||
|
copyFileToDir from@(File name _, _) to@(Dir {}, _) = do
|
||||||
|
let dp = getFullPath to
|
||||||
|
dirSanityThrow dp
|
||||||
|
copyFile from (dp </> name)
|
||||||
|
copyFileToDir from (Dir {}, _) = throw $ NotAFile (getFullPath from)
|
||||||
|
copyFileToDir _ to = throw $ NotADir (getFullPath to)
|
||||||
|
|
||||||
|
|
||||||
|
-- |Copies the given file, regardless of whether the destination is
|
||||||
|
-- a file or a directory. This is a wrapper around `copyFile` and
|
||||||
|
-- `copyFileToDir`.
|
||||||
|
easyCopyFile :: DTZipper a b -> Either FilePath (DTZipper a b) -> IO ()
|
||||||
|
easyCopyFile from (Left to) = copyFile from to
|
||||||
|
easyCopyFile from (Right to) = copyFileToDir from to
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given file.
|
-- |Deletes the given file.
|
||||||
@ -47,10 +115,12 @@ data FileOperation = Copy
|
|||||||
--
|
--
|
||||||
-- It also throws exceptions from `removeFile`.
|
-- It also throws exceptions from `removeFile`.
|
||||||
-- TODO: threaded, shouldn't block the GUI
|
-- TODO: threaded, shouldn't block the GUI
|
||||||
deleteFile :: FilePath -> IO ()
|
deleteFile :: DTZipper a b -> IO ()
|
||||||
deleteFile fp = do
|
deleteFile dtz@(File {}, _) = do
|
||||||
|
let fp = getFullPath dtz
|
||||||
fileSanityThrow fp
|
fileSanityThrow fp
|
||||||
removeFile fp
|
removeFile fp
|
||||||
|
deleteFile dtz = throw $ NotAFile (getFullPath dtz)
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given directory.
|
-- |Deletes the given directory.
|
||||||
@ -60,21 +130,31 @@ deleteFile fp = do
|
|||||||
--
|
--
|
||||||
-- It also throws exceptions from `removeDirectory`.
|
-- It also throws exceptions from `removeDirectory`.
|
||||||
-- TODO: threaded, shouldn't block the GUI
|
-- TODO: threaded, shouldn't block the GUI
|
||||||
deleteDir :: FilePath -> IO ()
|
deleteDir :: DTZipper a b -> IO ()
|
||||||
deleteDir fp = do
|
deleteDir dtz@(Dir {}, _) = do
|
||||||
|
let fp = getFullPath dtz
|
||||||
dirSanityThrow fp
|
dirSanityThrow fp
|
||||||
removeDirectory fp
|
removeDirectory fp
|
||||||
|
deleteDir dtz = throw $ NotADir (getFullPath dtz)
|
||||||
|
|
||||||
|
|
||||||
|
-- |Deletes a file or directory, whatever it may be.
|
||||||
|
easyDelete :: DTZipper a b -> IO ()
|
||||||
|
easyDelete dtz@(File {}, _) = deleteFile dtz
|
||||||
|
easyDelete dtz@(Dir {}, _) = deleteDir dtz
|
||||||
|
|
||||||
|
|
||||||
-- |Opens a file appropriately by invoking xdg-open.
|
-- |Opens a file appropriately by invoking xdg-open.
|
||||||
--
|
--
|
||||||
-- This will throw an exception if the filepath is not absolute
|
-- This will throw an exception if the filepath is not absolute
|
||||||
-- or the file does not exist.
|
-- or the file does not exist.
|
||||||
openFile :: FilePath -- ^ absolute path to file
|
openFile :: DTZipper a b
|
||||||
-> IO ProcessHandle
|
-> IO ProcessHandle
|
||||||
openFile fp = do
|
openFile dtz@(File {}, _) = do
|
||||||
|
let fp = getFullPath dtz
|
||||||
fileSanityThrow fp
|
fileSanityThrow fp
|
||||||
spawnProcess "xdg-open" [fp]
|
spawnProcess "xdg-open" [fp]
|
||||||
|
openFile dtz = throw $ NotAFile (getFullPath dtz)
|
||||||
|
|
||||||
|
|
||||||
-- |Executes a program with the given arguments.
|
-- |Executes a program with the given arguments.
|
||||||
@ -82,29 +162,14 @@ openFile fp = do
|
|||||||
-- This will throw an exception if the filepath is not absolute
|
-- This will throw an exception if the filepath is not absolute
|
||||||
-- or the file does not exist. It will also throw an exception
|
-- or the file does not exist. It will also throw an exception
|
||||||
-- if the file is not executable.
|
-- if the file is not executable.
|
||||||
executeFile :: FilePath -- ^ absolute path to program
|
executeFile :: DTZipper a b -- ^ program
|
||||||
-> [String] -- ^ arguments
|
-> [String] -- ^ arguments
|
||||||
-> IO ProcessHandle
|
-> IO ProcessHandle
|
||||||
executeFile fp args = do
|
executeFile dtz@(File {}, _) args = do
|
||||||
|
let fp = getFullPath dtz
|
||||||
fileSanityThrow fp
|
fileSanityThrow fp
|
||||||
p <- getPermissions fp
|
p <- getPermissions fp
|
||||||
unless (executable p) (throw $ FileNotExecutable fp)
|
unless (executable p) (throw $ FileNotExecutable fp)
|
||||||
spawnProcess fp args
|
spawnProcess fp args
|
||||||
|
executeFile dtz _ = throw $ NotAFile (getFullPath dtz)
|
||||||
|
|
||||||
|
|
||||||
-- Throws an exception if the filepath is not absolute
|
|
||||||
-- or the file does not exist.
|
|
||||||
fileSanityThrow :: FilePath -> IO ()
|
|
||||||
fileSanityThrow fp = do
|
|
||||||
unless (isAbsolute fp) (throw $ PathNotAbsolute fp)
|
|
||||||
exists <- doesFileExist fp
|
|
||||||
unless exists (throw $ FileDoesNotExist fp)
|
|
||||||
|
|
||||||
|
|
||||||
-- Throws an exception if the filepath is not absolute
|
|
||||||
-- or the dir does not exist.
|
|
||||||
dirSanityThrow :: FilePath -> IO ()
|
|
||||||
dirSanityThrow fp = do
|
|
||||||
unless (isAbsolute fp) (throw $ PathNotAbsolute fp)
|
|
||||||
exists <- doesDirectoryExist fp
|
|
||||||
unless exists (throw $ FileDoesNotExist fp)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user