packages feed

safer-file-handles-0.4: System/IO/SaferFileHandles/Internal.hs

{-# 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 <v.dijk.bas@gmail.com>
--
-------------------------------------------------------------------------------

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 ---------------------------------------------------------------------