I am trying to handle file errors manually so I can print my own message and currently I have this code:
handleFileError :: FileError -> IO a
handleFileError (FileError errorKind) = do
case errorKind of
NotFound -> undefined
NoPermission -> undefined
IsDirectory -> undefined
fileRead :: String -> IO String
fileRead file = do
pathExists <- doesPathExist file
notDirectory <- doesFileExist file
-- These two must be handled before `System.Directory.getPermissions` is called
-- or else it will error.
permissions <- getPermissions file
let hasReadPermissions = readable permissions
if hasReadPermissions then undefined -- This is the success case
else handleFileError $ FileError NoPermissions
I would like to check if any of the 3 booleans (pathExists, notDirectory, and hasReadPermissions) are false, and then act accordingly. I tried to implement this using a case with False
, however this just always runs the first branch.
CodePudding user response:
I have solved this - I didnt realise that it was possible to nest if
statements in a way that functions like else-if in other languages:
if not (pathExists) then handleFileError $ FileError NotFound
else if not (notDirectory) then handleFileError $ FileError IsDirectory
else do
permissions <- getPermissions file
let hasReadPermissions = readable permissions
if hasReadPermissions then undefined -- success
else handleFileERror $ FileError NoPermissions
CodePudding user response:
One way would be to use MultiWayIf
:
do
pathExists <- doesPathExist file
notDirectory <- doesFileExist file
permissions <- unsafeInterleaveIO (getPermissions file)
if
| not pathExists -> -- ...
| not notDirectory -> -- ...
| not permissions -> -- ...
| otherwise -> -- ...
If you're allergic to extensions, the old-fashioned way to get this feature is using guards, as in:
case () of
_ | not pathExists -> -- ...
| not notDirectory -> -- ...
| not permissions -> -- ...
| otherwise -> -- ...
But I recommend neither of these. Instead, just do something with the file, and catch the exception; otherwise there are race conditions with the file system changing out from under you between the check and the file use. Like this:
fileRead :: String -> IO String
fileRead = catch (undefined {- success case -}) $ \e -> if
| isDoesNotExistError e -> -- ...
| isPermissionError e -> -- ...
| otherwise -> throw e