on master help sign in
Expose filesystem file module (#2162)
dae14c5Ranjeet Ranjan
core/src/Streamly/FileSystem/File.hsnew file+56
@@ -0,0 +1,56 @@
1+-- |
2+-- Module : Streamly.FileSystem.File
3+-- Copyright : (c) 2019 Composewell Technologies
4+--
5+-- License : BSD3
6+-- Maintainer : streamly@composewell.com
7+-- Stability : pre-release
8+-- Portability : GHC
9+--
10+-- Read and write streams and arrays to and from files specified by their paths
11+-- in the file system. Unlike the handle based APIs which can have a read/write
12+-- session consisting of multiple reads and writes to the handle, these APIs
13+-- are one shot read or write APIs. These APIs open the file handle, perform
14+-- the requested operation and close the handle. Thease are safer compared to
15+-- the handle based APIs as there is no possibility of a file descriptor
16+-- leakage.
17+--
18+-- > import qualified Streamly.FileSystem.File as File
19+--
20+module Streamly.FileSystem.File
21+ (
22+ -- * Streaming IO
23+ -- | Stream data to or from a file or device sequentially. When reading,
24+ -- the stream is lazy and generated on-demand as the consumer consumes it.
25+ -- Read IO requests to the IO device are performed in chunks limited to a
26+ -- maximum size of 32KiB, this is referred to as @defaultChunkSize@ in the
27+ -- documentation. One IO request may or may not read the full
28+ -- chunk. If the whole stream is not consumed, it is possible that we may
29+ -- read slightly more from the IO device than what the consumer needed.
30+ -- Unless specified otherwise in the API, writes are collected into chunks
31+ -- of @defaultChunkSize@ before they are written to the IO device.
32+
33+ -- Streaming APIs work for all kind of devices, seekable or non-seekable;
34+ -- including disks, files, memory devices, terminals, pipes, sockets and
35+ -- fifos. While random access APIs work only for files or devices that have
36+ -- random access or seek capability for example disks, memory devices.
37+ -- Devices like terminals, pipes, sockets and fifos do not have random
38+ -- access capability.
39+
40+ -- ** File IO Using Handle
41+ withFile
42+
43+ -- ** Streams
44+ , read
45+ , readChunksWith
46+ , readChunks
47+
48+ -- ** Folds
49+ , write
50+ , writeWith
51+ , writeChunks
52+ )
53+where
54+
55+import Streamly.Internal.FileSystem.File
56+import Prelude hiding (read)
core/src/Streamly/Internal/FileSystem/File.hs+36−27
@@ -147,7 +147,7 @@ import qualified Streamly.Data.Stream as S (fold, bracketIO, mapM)
147-- /Pre-release/
147-- /Pre-release/
148--
148--
149{-# INLINE withFile #-}
149{-# INLINE withFile #-}
150−withFile :: (MonadCatch m, MonadIO m)
150+withFile :: (MonadIO m, MonadCatch m)
151 => FilePath -> IOMode -> (Handle -> Stream m a) -> Stream m a
151 => FilePath -> IOMode -> (Handle -> Stream m a) -> Stream m a
152withFile file mode = S.bracketIO (openFile file mode) hClose
152withFile file mode = S.bracketIO (openFile file mode) hClose
153
153
@@ -160,12 +160,12 @@ withFile file mode = S.bracketIO (openFile file mode) hClose
160-- /Pre-release/
160-- /Pre-release/
161--
161--
162{-# INLINE usingFile #-}
162{-# INLINE usingFile #-}
163−usingFile :: (MonadCatch m, MonadIO m)
163+usingFile :: (MonadIO m, MonadCatch m)
164 => Unfold m Handle a -> Unfold m FilePath a
164 => Unfold m Handle a -> Unfold m FilePath a
165usingFile = UF.bracketIO (`openFile` ReadMode) hClose
165usingFile = UF.bracketIO (`openFile` ReadMode) hClose
166
166
167{-# INLINE usingFile2 #-}
167{-# INLINE usingFile2 #-}
168−usingFile2 :: (MonadCatch m, MonadIO m)
168+usingFile2 :: (MonadIO m, MonadCatch m)
169 => Unfold m (x, Handle) a -> Unfold m (x, FilePath) a
169 => Unfold m (x, Handle) a -> Unfold m (x, FilePath) a
170usingFile2 = UF.bracketIO before after
170usingFile2 = UF.bracketIO before after
171
171
@@ -178,7 +178,7 @@ usingFile2 = UF.bracketIO before after
178 after (_, h) = hClose h
178 after (_, h) = hClose h
179
179
180{-# INLINE usingFile3 #-}
180{-# INLINE usingFile3 #-}
181−usingFile3 :: (MonadCatch m, MonadIO m)
181+usingFile3 :: (MonadIO m, MonadCatch m)
182 => Unfold m (x, y, z, Handle) a -> Unfold m (x, y, z, FilePath) a
182 => Unfold m (x, y, z, Handle) a -> Unfold m (x, y, z, FilePath) a
183usingFile3 = UF.bracketIO before after
183usingFile3 = UF.bracketIO before after
184
184
@@ -202,14 +202,16 @@ usingFile3 = UF.bracketIO before after
202
202
203-- | Write an array to a file. Overwrites the file if it exists.
203-- | Write an array to a file. Overwrites the file if it exists.
204--
204--
205−-- @since 0.7.0
205+-- /Pre-release/
206+--
206{-# INLINABLE putChunk #-}
207{-# INLINABLE putChunk #-}
207putChunk :: FilePath -> Array a -> IO ()
208putChunk :: FilePath -> Array a -> IO ()
208putChunk file arr = SIO.withFile file WriteMode (`FH.putChunk` arr)
209putChunk file arr = SIO.withFile file WriteMode (`FH.putChunk` arr)
209
210
210-- | append an array to a file.
211-- | append an array to a file.
211--
212--
212−-- @since 0.7.0
213+-- /Pre-release/
214+--
213{-# INLINABLE appendArray #-}
215{-# INLINABLE appendArray #-}
214appendArray :: FilePath -> Array a -> IO ()
216appendArray :: FilePath -> Array a -> IO ()
215appendArray file arr = SIO.withFile file AppendMode (`FH.putChunk` arr)
217appendArray file arr = SIO.withFile file AppendMode (`FH.putChunk` arr)
@@ -222,16 +224,17 @@ appendArray file arr = SIO.withFile file AppendMode (`FH.putChunk` arr)
222-- The maximum size of a single array is specified by @size@. The actual size
224-- The maximum size of a single array is specified by @size@. The actual size
223-- read may be less than or equal to @size@.
225-- read may be less than or equal to @size@.
224--
226--
225−-- @since 0.9.0
227+-- /Pre-release/
228+--
226{-# INLINE readChunksWith #-}
229{-# INLINE readChunksWith #-}
227−readChunksWith :: (MonadCatch m, MonadIO m)
230+readChunksWith :: (MonadIO m, MonadCatch m)
228 => Int -> FilePath -> Stream m (Array Word8)
231 => Int -> FilePath -> Stream m (Array Word8)
229readChunksWith size file =
232readChunksWith size file =
230 withFile file ReadMode (FH.readChunksWith size)
233 withFile file ReadMode (FH.readChunksWith size)
231
234
232{-# DEPRECATED toChunksWithBufferOf "Please use 'readChunksWith' instead" #-}
235{-# DEPRECATED toChunksWithBufferOf "Please use 'readChunksWith' instead" #-}
233{-# INLINE toChunksWithBufferOf #-}
236{-# INLINE toChunksWithBufferOf #-}
234−toChunksWithBufferOf :: (MonadCatch m, MonadIO m)
237+toChunksWithBufferOf :: (MonadIO m, MonadCatch m)
235 => Int -> FilePath -> Stream m (Array Word8)
238 => Int -> FilePath -> Stream m (Array Word8)
236toChunksWithBufferOf = readChunksWith
239toChunksWithBufferOf = readChunksWith
237
240
@@ -243,15 +246,16 @@ toChunksWithBufferOf = readChunksWith
243--
246--
244-- > readChunks = readChunksWith defaultChunkSize
247-- > readChunks = readChunksWith defaultChunkSize
245--
248--
246−-- @since 0.7.0
249+-- /Pre-release/
250+--
247{-# INLINE readChunks #-}
251{-# INLINE readChunks #-}
248−readChunks :: (MonadCatch m, MonadIO m)
252+readChunks :: (MonadIO m, MonadCatch m)
249 => FilePath -> Stream m (Array Word8)
253 => FilePath -> Stream m (Array Word8)
250readChunks = readChunksWith defaultChunkSize
254readChunks = readChunksWith defaultChunkSize
251
255
252{-# DEPRECATED toChunks "Please use 'readChunks' instead" #-}
256{-# DEPRECATED toChunks "Please use 'readChunks' instead" #-}
253{-# INLINE toChunks #-}
257{-# INLINE toChunks #-}
254−toChunks :: (MonadCatch m, MonadIO m) => FilePath -> Stream m (Array Word8)
258+toChunks :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Array Word8)
255toChunks = readChunks
259toChunks = readChunks
256
260
257-------------------------------------------------------------------------------
261-------------------------------------------------------------------------------
@@ -270,14 +274,14 @@ toChunks = readChunks
270-- /Pre-release/
274-- /Pre-release/
271--
275--
272{-# INLINE chunkReaderWith #-}
276{-# INLINE chunkReaderWith #-}
273−chunkReaderWith :: (MonadCatch m, MonadIO m)
277+chunkReaderWith :: (MonadIO m, MonadCatch m)
274 => Unfold m (Int, FilePath) (Array Word8)
278 => Unfold m (Int, FilePath) (Array Word8)
275chunkReaderWith = usingFile2 FH.chunkReaderWith
279chunkReaderWith = usingFile2 FH.chunkReaderWith
276
280
277{-# DEPRECATED readChunksWithBufferOf
281{-# DEPRECATED readChunksWithBufferOf
278 "Please use 'chunkReaderWith' instead" #-}
282 "Please use 'chunkReaderWith' instead" #-}
279{-# INLINE readChunksWithBufferOf #-}
283{-# INLINE readChunksWithBufferOf #-}
280−readChunksWithBufferOf :: (MonadCatch m, MonadIO m)
284+readChunksWithBufferOf :: (MonadIO m, MonadCatch m)
281 => Unfold m (Int, FilePath) (Array Word8)
285 => Unfold m (Int, FilePath) (Array Word8)
282readChunksWithBufferOf = chunkReaderWith
286readChunksWithBufferOf = chunkReaderWith
283
287
@@ -290,14 +294,14 @@ readChunksWithBufferOf = chunkReaderWith
290--
294--
291-- /Pre-release/
295-- /Pre-release/
292{-# INLINE chunkReaderFromToWith #-}
296{-# INLINE chunkReaderFromToWith #-}
293−chunkReaderFromToWith :: (MonadCatch m, MonadIO m) =>
297+chunkReaderFromToWith :: (MonadIO m, MonadCatch m) =>
294 Unfold m (Int, Int, Int, FilePath) (Array Word8)
298 Unfold m (Int, Int, Int, FilePath) (Array Word8)
295chunkReaderFromToWith = usingFile3 FH.chunkReaderFromToWith
299chunkReaderFromToWith = usingFile3 FH.chunkReaderFromToWith
296
300
297{-# DEPRECATED readChunksFromToWith
301{-# DEPRECATED readChunksFromToWith
298 "Please use 'chunkReaderFromToWith' instead" #-}
302 "Please use 'chunkReaderFromToWith' instead" #-}
299{-# INLINE readChunksFromToWith #-}
303{-# INLINE readChunksFromToWith #-}
300−readChunksFromToWith :: (MonadCatch m, MonadIO m) =>
304+readChunksFromToWith :: (MonadIO m, MonadCatch m) =>
301 Unfold m (Int, Int, Int, FilePath) (Array Word8)
305 Unfold m (Int, Int, Int, FilePath) (Array Word8)
302readChunksFromToWith = chunkReaderFromToWith
306readChunksFromToWith = chunkReaderFromToWith
303
307
@@ -309,7 +313,7 @@ readChunksFromToWith = chunkReaderFromToWith
309--
313--
310-- /Pre-release/
314-- /Pre-release/
311{-# INLINE chunkReader #-}
315{-# INLINE chunkReader #-}
312−chunkReader :: (MonadCatch m, MonadIO m) => Unfold m FilePath (Array Word8)
316+chunkReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath (Array Word8)
313chunkReader = usingFile FH.chunkReader
317chunkReader = usingFile FH.chunkReader
314
318
315-- | Unfolds the tuple @(bufsize, filepath)@ into a byte stream, read requests
319-- | Unfolds the tuple @(bufsize, filepath)@ into a byte stream, read requests
@@ -317,12 +321,12 @@ chunkReader = usingFile FH.chunkReader
317--
321--
318-- /Pre-release/
322-- /Pre-release/
319{-# INLINE readerWith #-}
323{-# INLINE readerWith #-}
320−readerWith :: (MonadCatch m, MonadIO m) => Unfold m (Int, FilePath) Word8
324+readerWith :: (MonadIO m, MonadCatch m) => Unfold m (Int, FilePath) Word8
321readerWith = usingFile2 FH.readerWith
325readerWith = usingFile2 FH.readerWith
322
326
323{-# DEPRECATED readWithBufferOf "Please use 'readerWith' instead" #-}
327{-# DEPRECATED readWithBufferOf "Please use 'readerWith' instead" #-}
324{-# INLINE readWithBufferOf #-}
328{-# INLINE readWithBufferOf #-}
325−readWithBufferOf :: (MonadCatch m, MonadIO m) =>
329+readWithBufferOf :: (MonadIO m, MonadCatch m) =>
326 Unfold m (Int, FilePath) Word8
330 Unfold m (Int, FilePath) Word8
327readWithBufferOf = readerWith
331readWithBufferOf = readerWith
328
332
@@ -332,7 +336,7 @@ readWithBufferOf = readerWith
332--
336--
333-- /Pre-release/
337-- /Pre-release/
334{-# INLINE reader #-}
338{-# INLINE reader #-}
335−reader :: (MonadCatch m, MonadIO m) => Unfold m FilePath Word8
339+reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8
336reader = UF.many A.reader (usingFile FH.chunkReader)
340reader = UF.many A.reader (usingFile FH.chunkReader)
337
341
338-- | Generate a stream of bytes from a file specified by path. The stream ends
342-- | Generate a stream of bytes from a file specified by path. The stream ends
@@ -342,12 +346,12 @@ reader = UF.many A.reader (usingFile FH.chunkReader)
342-- /Pre-release/
346-- /Pre-release/
343--
347--
344{-# INLINE read #-}
348{-# INLINE read #-}
345−read :: (MonadCatch m, MonadIO m) => FilePath -> Stream m Word8
349+read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
346read file = AS.concat $ withFile file ReadMode FH.readChunks
350read file = AS.concat $ withFile file ReadMode FH.readChunks
347
351
348{-# DEPRECATED toBytes "Please use 'read' instead" #-}
352{-# DEPRECATED toBytes "Please use 'read' instead" #-}
349{-# INLINE toBytes #-}
353{-# INLINE toBytes #-}
350−toBytes :: (MonadCatch m, MonadIO m) => FilePath -> Stream m Word8
354+toBytes :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
351toBytes = read
355toBytes = read
352
356
353{-
357{-
@@ -373,7 +377,8 @@ fromChunksMode mode file xs = S.fold drain $
373
377
374-- | Write a stream of arrays to a file. Overwrites the file if it exists.
378-- | Write a stream of arrays to a file. Overwrites the file if it exists.
375--
379--
376−-- @since 0.7.0
380+-- /Pre-release/
381+--
377{-# INLINE fromChunks #-}
382{-# INLINE fromChunks #-}
378fromChunks :: (MonadIO m, MonadCatch m)
383fromChunks :: (MonadIO m, MonadCatch m)
379 => FilePath -> Stream m (Array a) -> m ()
384 => FilePath -> Stream m (Array a) -> m ()
@@ -391,7 +396,8 @@ fromChunks = fromChunksMode WriteMode
391-- be written to the IO device as soon as we collect the specified number of
396-- be written to the IO device as soon as we collect the specified number of
392-- input elements.
397-- input elements.
393--
398--
394−-- @since 0.9.0
399+-- /Pre-release/
400+--
395{-# INLINE fromBytesWith #-}
401{-# INLINE fromBytesWith #-}
396fromBytesWith :: (MonadIO m, MonadCatch m)
402fromBytesWith :: (MonadIO m, MonadCatch m)
397 => Int -> FilePath -> Stream m Word8 -> m ()
403 => Int -> FilePath -> Stream m Word8 -> m ()
@@ -476,7 +482,8 @@ write = writeWith defaultChunkSize
476
482
477-- | Append a stream of arrays to a file.
483-- | Append a stream of arrays to a file.
478--
484--
479−-- @since 0.7.0
485+-- /Pre-release/
486+--
480{-# INLINE appendChunks #-}
487{-# INLINE appendChunks #-}
481appendChunks :: (MonadIO m, MonadCatch m)
488appendChunks :: (MonadIO m, MonadCatch m)
482 => FilePath -> Stream m (Array a) -> m ()
489 => FilePath -> Stream m (Array a) -> m ()
@@ -486,7 +493,8 @@ appendChunks = fromChunksMode AppendMode
486-- be written to the IO device as soon as we collect the specified number of
493-- be written to the IO device as soon as we collect the specified number of
487-- input elements.
494-- input elements.
488--
495--
489−-- @since 0.9.0
496+-- /Pre-release/
497+--
490{-# INLINE appendWith #-}
498{-# INLINE appendWith #-}
491appendWith :: (MonadIO m, MonadCatch m)
499appendWith :: (MonadIO m, MonadCatch m)
492 => Int -> FilePath -> Stream m Word8 -> m ()
500 => Int -> FilePath -> Stream m Word8 -> m ()
@@ -497,7 +505,8 @@ appendWith n file xs = appendChunks file $ AS.arraysOf n xs
497-- is appended to the file. If the file does not exist it is created. File is
505-- is appended to the file. If the file does not exist it is created. File is
498-- locked using single writer locking mode.
506-- locked using single writer locking mode.
499--
507--
500−-- @since 0.7.0
508+-- /Pre-release/
509+--
501{-# INLINE append #-}
510{-# INLINE append #-}
502append :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8 -> m ()
511append :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8 -> m ()
503append = appendWith defaultChunkSize
512append = appendWith defaultChunkSize
core/streamly-core.cabal+1
@@ -390,6 +390,7 @@ library
390 , Streamly.Data.Stream
390 , Streamly.Data.Stream
391 , Streamly.Data.Unfold
391 , Streamly.Data.Unfold
392 , Streamly.FileSystem.Dir
392 , Streamly.FileSystem.Dir
393+ , Streamly.FileSystem.File
393 , Streamly.FileSystem.Handle
394 , Streamly.FileSystem.Handle
394 , Streamly.Unicode.Parser
395 , Streamly.Unicode.Parser
395 , Streamly.Unicode.Stream
396 , Streamly.Unicode.Stream