streamly-filesystem/src/Streamly/External/FileSystem/Handle/Posix.hs

141 lines
5.0 KiB
Haskell
Raw Normal View History

2020-01-29 15:38:35 +00:00
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiWayIf #-}
-- |
-- Module : Streamly.External.FileSystem.Handle.Posix
-- Copyright : © 2020 Julian Ospald
-- License : BSD3
--
-- Maintainer : Julian Ospald <hasufell@posteo.de>
-- Stability : experimental
-- Portability : portable
--
-- This module provides high-level file streaming API.
2020-01-29 16:25:53 +00:00
module Streamly.External.FileSystem.Handle.Posix
2020-01-29 17:24:26 +00:00
(
-- * File reading
readFileLBS
2020-01-29 16:25:53 +00:00
, readFileStream
2020-01-29 17:24:26 +00:00
-- * File writing
2020-01-29 16:25:53 +00:00
, copyFileHandle
, copyFileStream
2020-01-29 17:24:26 +00:00
-- * Directory listing
2020-01-29 16:25:53 +00:00
, unfoldDirContents
, dirContentsStream
2020-01-29 17:24:26 +00:00
, dirContents
2020-01-29 16:25:53 +00:00
, DirType
)
where
2020-01-29 15:38:35 +00:00
2020-01-29 19:21:01 +00:00
import Control.Exception.Safe
2020-01-29 15:38:35 +00:00
import Control.Monad.IO.Class ( liftIO
, MonadIO
)
import Data.Word ( Word8 )
import Data.Word8
2020-01-29 19:21:01 +00:00
import Prelude hiding ( readFile )
import Streamly
2020-01-29 15:38:35 +00:00
import Streamly.Internal.Data.Unfold.Types
2020-01-29 19:21:01 +00:00
import Streamly.Memory.Array
import System.IO ( Handle
, hClose
)
import System.IO.Unsafe
import System.Posix.ByteString
import System.Posix.Directory.ByteString
as PosixBS
import System.Posix.Foreign ( DirType )
2020-01-29 15:38:35 +00:00
import System.Posix.RawFilePath.Directory.Traversals
2020-01-29 16:25:53 +00:00
hiding ( getDirectoryContents )
2020-01-29 19:21:01 +00:00
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Internal as BSLI
import qualified Streamly.External.ByteString as Strict
import qualified Streamly.FileSystem.Handle as FH
2020-01-29 15:38:35 +00:00
import qualified Streamly.Internal.Data.Stream.StreamD.Type
as D
2020-01-29 19:21:01 +00:00
import qualified Streamly.Internal.Data.Unfold as SIU
import qualified Streamly.Internal.Prelude as S
2020-01-29 15:38:35 +00:00
-- |Read the given file lazily as a lazy ByteString.
--
-- The handle is closed automatically, when the stream exits normally,
-- aborts or gets garbage collected.
--
-- This uses `unsafeInterleaveIO` under the hood.
2020-01-29 16:25:53 +00:00
readFileLBS :: Handle -- ^ readable file handle
-> IO L.ByteString
readFileLBS handle' = fromChunks (readFileStream handle')
2020-01-29 15:38:35 +00:00
where
-- https://github.com/psibi/streamly-bytestring/issues/7
fromChunks =
S.foldrM (\x b -> unsafeInterleaveIO b >>= pure . BSLI.chunk x)
(pure BSLI.Empty)
. S.map Strict.fromArray
-- | Read from the given handle as a streamly filestream.
--
-- The handle is closed automatically, when the stream exits normally,
-- aborts or gets garbage collected.
readFileStream :: (MonadCatch m, MonadAsync m)
=> Handle
-> SerialT m (Array Word8)
2020-01-29 19:21:01 +00:00
readFileStream = S.unfold (SIU.finallyIO (liftIO . hClose) FH.readChunks)
2020-01-29 15:38:35 +00:00
-- | Like 'copyFileStream', except for two file handles.
2020-01-29 16:25:53 +00:00
copyFileHandle :: (MonadCatch m, MonadAsync m, MonadMask m)
=> Handle -- ^ copy from this handle, must be readable
-> Handle -- ^ copy to this handle, must be writable
-> m ()
copyFileHandle fromHandle toHandle =
2020-01-29 15:38:35 +00:00
copyFileStream (readFileStream fromHandle) toHandle
-- | Copy a stream to a file handle.
--
-- The handle is closed automatically after the stream is copied.
copyFileStream :: (MonadCatch m, MonadAsync m, MonadMask m)
=> SerialT m (Array Word8) -- ^ stream to copy
2020-01-29 16:25:53 +00:00
-> Handle -- ^ file handle to copy to, must be writable
2020-01-29 15:38:35 +00:00
-> m ()
copyFileStream stream handle' =
2020-01-29 19:21:01 +00:00
(flip finally) (liftIO $ hClose handle')
$ S.fold (FH.writeChunks handle') stream
2020-01-29 15:38:35 +00:00
2020-01-29 16:25:53 +00:00
-- | Create an 'Unfold' of directory contents.
unfoldDirContents :: MonadIO m => Unfold m DirStream (DirType, RawFilePath)
unfoldDirContents = Unfold step return
2020-01-29 15:38:35 +00:00
where
{-# INLINE [0] step #-}
step dirstream = do
(typ, e) <- liftIO $ readDirEnt dirstream
return if
| BS.null e -> D.Stop
2020-01-29 16:25:53 +00:00
| BS.pack [_period] == e -> D.Skip dirstream
2020-01-29 15:38:35 +00:00
| BS.pack [_period, _period] == e -> D.Skip dirstream
| otherwise -> D.Yield (typ, e) dirstream
2020-01-29 16:25:53 +00:00
-- | Read the directory contents as a stream.
--
-- The DirStream is closed automatically, when the streamly stream exits
-- normally, aborts or gets garbage collected.
dirContentsStream :: (MonadCatch m, MonadAsync m, MonadMask m)
=> DirStream
-> SerialT m (DirType, RawFilePath)
dirContentsStream =
S.unfold (SIU.finallyIO (liftIO . PosixBS.closeDirStream) unfoldDirContents)
2020-01-29 17:24:26 +00:00
-- | Read the directory contents strictly as a list.
--
-- The DirStream is closed automatically.
dirContents :: (MonadCatch m, MonadAsync m, MonadMask m)
=> DirStream
-> m [(DirType, RawFilePath)]
2020-01-29 19:21:01 +00:00
dirContents = S.toList . dirContentsStream