-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathCommon.hs
More file actions
97 lines (85 loc) · 2.97 KB
/
Common.hs
File metadata and controls
97 lines (85 loc) · 2.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
module Streamly.Internal.FileSystem.File.Common
( withFile
, openFile
) where
-------------------------------------------------------------------------------
-- Imports
-------------------------------------------------------------------------------
import Control.Exception (mask, onException, try)
import Control.Monad (when)
import GHC.IO (catchException)
import GHC.IO.Exception (IOException(..))
import GHC.IO.Handle.Internals (handleFinalizer)
import Streamly.Internal.FileSystem.Path (Path)
import System.IO (IOMode(..), Handle, hSetBinaryMode, hClose)
import qualified Streamly.Internal.FileSystem.Path as Path
#if MIN_VERSION_base(4,16,0)
import GHC.IO.Handle.Internals (addHandleFinalizer)
#else
import Control.Concurrent.MVar (MVar, addMVarFinalizer)
import GHC.IO.Handle.Types (Handle__, Handle(..))
#endif
-------------------------------------------------------------------------------
-- Utils
-------------------------------------------------------------------------------
#if !(MIN_VERSION_base(4,16,0))
type HandleFinalizer = FilePath -> MVar Handle__ -> IO ()
-- | Add a finalizer to a 'Handle'. Specifically, the finalizer
-- will be added to the 'MVar' of a file handle or the write-side
-- 'MVar' of a duplex handle. See Handle Finalizers for details.
addHandleFinalizer :: Handle -> HandleFinalizer -> IO ()
addHandleFinalizer handle finalizer = do
addMVarFinalizer mv (finalizer filepath mv)
where
!(filepath, !mv) = case handle of
FileHandle fp m -> (fp, m)
DuplexHandle fp _ write_m -> (fp, write_m)
#endif
{-# INLINE withOpenFile #-}
withOpenFile
:: Bool
-> Bool
-> (Path -> IOMode -> IO Handle)
-> Path
-> IOMode
-> (Handle -> IO r)
-> IO r
withOpenFile binary close_finally f fp iomode action =
mask $ \restore -> do
h <- f fp iomode
-- XXX In case of withFile it will be closed anyway, so do we even need
-- this?
addHandleFinalizer h handleFinalizer
when binary $ hSetBinaryMode h True
r <- restore (action h) `onException` hClose h
when close_finally $ hClose h
pure r
addFilePathToIOError :: String -> Path -> IOException -> IOException
addFilePathToIOError fun fp ioe =
let !str = Path.toString fp
in ioe
{ ioe_location = fun
, ioe_filename = Just str
}
{-# INLINE catchWith #-}
catchWith :: String -> Path -> IO a -> IO a
catchWith str path io =
catchException io (ioError . addFilePathToIOError str path)
{-# INLINE withFile #-}
withFile ::
Bool
-> (Path -> IOMode -> IO Handle)
-> Path
-> IOMode
-> (Handle -> IO r)
-> IO r
withFile binary f path iomode act =
catchWith "withFile" path
(withOpenFile binary True f path iomode (try . act))
>>= either ioError pure
{-# INLINE openFile #-}
openFile ::
Bool -> (Path -> IOMode -> IO Handle) -> Path -> IOMode -> IO Handle
openFile binary f path iomode =
catchWith "openFile" path
$ withOpenFile binary False f path iomode pure