{-# LANGUAGE UnicodeSyntax , NoImplicitPrelude , CPP , GADTs , TypeFamilies , RankNTypes , MultiParamTypeClasses #-} ------------------------------------------------------------------------------- -- | -- Module : System.IO.Internal -- Copyright : (c) 2009 Bas van Dijk -- License : BSD3 (see the file LICENSE) -- Maintainer : Bas van Dijk -- ------------------------------------------------------------------------------- module System.IO.SaferFileHandles.Internal where -- from base: import Control.Applicative ( (<$>) ) import Data.Function ( ($) ) import Data.Tuple ( uncurry ) import Data.Bool ( Bool(False, True) ) import Data.Char ( String ) import Data.Maybe ( Maybe(Nothing, Just) ) import System.IO.Error ( modifyIOError ) import GHC.IO.Exception ( ioe_handle ) -- from base-unicode-symbols: import Data.Function.Unicode ( (∘) ) -- from transformers: import Control.Monad.Trans ( MonadIO, liftIO ) -- from regions: import Control.Resource ( Resource, openResource, closeResource ) import qualified Control.Resource as R ( Handle ) import Control.Monad.Trans.Region ( RegionalHandle ) import Control.Monad.Trans.Region.Unsafe ( internalHandle ) -- from explicit-iomodes import System.IO.ExplicitIOModes ( IOMode(..), RW, IO, FilePath ) import qualified System.IO.ExplicitIOModes as E ( Handle , openFile , openBinaryFile , openTempFile , openBinaryTempFile #if MIN_VERSION_base(4,2,0) , openTempFileWithDefaultPermissions , openBinaryTempFileWithDefaultPermissions #endif , hClose ) ------------------------------------------------------------------------------- -- * Files with explicit IO modes as scarce resources ------------------------------------------------------------------------------- {-| A file scarce resource parameterized by the 'IOMode' in which you want to open the file. Note that this module provides an instance for 'Resource' for 'File' @ioMode@. This allows you to 'open' files in a region which are automatically closed when the region terminates but it disallows you to return handles to these closed files from the region so preventing I/O with closed files. -} data File ioMode where File ∷ Binary → FilePath → IOMode ioMode → File ioMode TempFile ∷ Binary → FilePath → Template #if MIN_VERSION_base(4,2,0) → DefaultPermissions #endif → File RW -- | Should the file be opened in binary mode? type Binary = Bool -- | The template of a temporary file path. type Template = String #if MIN_VERSION_base(4,2,0) -- | Should default permissions be used when opening a temporary file? type DefaultPermissions = Bool #endif instance Resource (File ioMode) where data R.Handle (File ioMode) = FileHandle { mbFilePath ∷ Maybe FilePath -- ^ Get the optional file path. This is needed because -- opening a temporary file also yields the generated file -- path. , handle ∷ E.Handle ioMode } openResource (File isBinary filePath ioMode) = FileHandle Nothing <$> (if isBinary then E.openBinaryFile else E.openFile) filePath ioMode #if MIN_VERSION_base(4,2,0) openResource (TempFile isBinary filePath template defaultPerms) = do uncurry (FileHandle ∘ Just) <$> (case (isBinary, defaultPerms) of (False, False) → E.openTempFile (True, False) → E.openBinaryTempFile (False, True) → E.openTempFileWithDefaultPermissions (True, True) → E.openBinaryTempFileWithDefaultPermissions ) filePath template #else openResource (TempFile isBinary filePath template) = do uncurry (FileHandle ∘ Just) <$> (if isBinary then E.openBinaryTempFile else E.openTempFile) filePath template #endif closeResource = sanitizeIOError ∘ E.hClose ∘ handle -- | A handy type synonym for a regional handle to an opened file parameterized -- by the 'IOMode' in which you opened the file and the region in which it was -- created. type RegionalFileHandle ioMode r = RegionalHandle (File ioMode) r -------------------------------------------------------------------------------- -- Utility wrapping functions -------------------------------------------------------------------------------- regularHandle ∷ RegionalFileHandle ioMode r → E.Handle ioMode regularHandle = handle ∘ internalHandle wrap ∷ MonadIO m ⇒ (E.Handle ioMode → IO α) → (RegionalFileHandle ioMode r → m α) wrap f = \h → liftIO $ sanitizeIOError $ f (regularHandle h) wrap2 ∷ MonadIO m ⇒ (E.Handle ioMode → β → IO α) → (RegionalFileHandle ioMode r → β → m α) wrap2 f = \h y → liftIO $ sanitizeIOError $ f (regularHandle h) y wrap3 ∷ MonadIO m ⇒ (E.Handle ioMode → γ → β → IO α) → (RegionalFileHandle ioMode r → γ → β → m α) wrap3 f = \h z y → liftIO $ sanitizeIOError $ f (regularHandle h) z y sanitizeIOError ∷ IO α → IO α sanitizeIOError = modifyIOError $ \e -> e { ioe_handle = Nothing } -- The End ---------------------------------------------------------------------