enumerator-0.4.11: src/io.anansi
\section{IO}
\subsection{Binary IO}
{\tt enumHandle} and {\tt enumFile} are rough analogues of
{\tt hGetContents} and {\tt readFile} from the standard library, except
they operate only in binary mode.
Any exceptions thrown while reading or writing data are caught and reported
using {\tt throwError}, so errors can be handled in pure iteratees.
:d binary IO
|apidoc Data.Enumerator.Binary.enumHandle|
enumHandle :: MonadIO m
=> Integer -- ^ Buffer size
-> IO.Handle
-> Enumerator B.ByteString m b
enumHandle bufferSize h = checkContinue0 $ \loop k -> do
let intSize = fromInteger bufferSize
bytes <- tryIO (getBytes h intSize)
if B.null bytes
then continue k
else k (Chunks [bytes]) >>== loop
:
:d binary IO
|apidoc Data.Enumerator.Binary.enumHandleRange|
enumHandleRange :: MonadIO m
=> Integer -- ^ Buffer size
-> Maybe Integer -- ^ Offset
-> Maybe Integer -- ^ Maximum count
-> IO.Handle
-> Enumerator B.ByteString m b
enumHandleRange bufferSize offset count h s = seek >> enum where
seek = case offset of
Nothing -> return ()
Just off -> tryIO (IO.hSeek h IO.AbsoluteSeek off)
enum = case count of
Just n -> enumRange n s
Nothing -> enumHandle bufferSize h s
enumRange = checkContinue1 $ \loop n k -> let
rem = fromInteger (min bufferSize n)
keepGoing = do
bytes <- tryIO (getBytes h rem)
if B.null bytes
then continue k
else feed bytes
feed bs = k (Chunks [bs]) >>== loop (n - (toInteger (B.length bs)))
in if rem <= 0
then continue k
else keepGoing
:
:d binary IO
getBytes :: IO.Handle -> Int -> IO B.ByteString
getBytes h n = do
hasInput <- Exc.catch
(IO.hWaitForInput h (-1))
(\err -> if isEOFError err
then return False
else Exc.throwIO err)
if hasInput
then B.hGetNonBlocking h n
else return B.empty
:
:d binary IO
|apidoc Data.Enumerator.Binary.enumFile|
enumFile :: FilePath -> Enumerator B.ByteString IO b
enumFile path = enumFileRange path Nothing Nothing
:
:d binary IO
|apidoc Data.Enumerator.Binary.enumFileRange|
enumFileRange :: FilePath
-> Maybe Integer -- ^ Offset
-> Maybe Integer -- ^ Maximum count
-> Enumerator B.ByteString IO b
enumFileRange path offset count step = do
h <- tryIO (IO.openBinaryFile path IO.ReadMode)
let iter = enumHandleRange 4096 offset count h step
Iteratee (Exc.finally (runIteratee iter) (IO.hClose h))
:
:d binary IO
|apidoc Data.Enumerator.Binary.iterHandle|
iterHandle :: MonadIO m => IO.Handle
-> Iteratee B.ByteString m ()
iterHandle h = continue step where
step EOF = yield () EOF
step (Chunks []) = continue step
step (Chunks bytes) = do
tryIO (CM.mapM_ (B.hPut h) bytes)
continue step
:
\subsection{Text IO}
Reading text is similar to reading bytes, but the enumerators have slightly
different behavior -- instead of reading in fixed-size chunks of data, the
text enumerators read in lines. This matches similar text-based {\sc api}s,
such as Python's {\tt xreadlines()}.
:d text IO
|apidoc Data.Enumerator.Text.enumHandle|
enumHandle :: MonadIO m => IO.Handle
-> Enumerator T.Text m b
enumHandle h = checkContinue0 $ \loop k -> do
let getText = Exc.catch
(Just `fmap` TIO.hGetLine h)
(\err -> if isEOFError err
then return Nothing
else Exc.throwIO err)
maybeText <- tryIO getText
case maybeText of
Nothing -> continue k
Just text -> k (Chunks [text]) >>== loop
:
:d text IO
|apidoc Data.Enumerator.Text.enumFile|
enumFile :: FilePath -> Enumerator T.Text IO b
enumFile path step = do
h <- tryIO (IO.openFile path IO.ReadMode)
Iteratee $ Exc.finally
(runIteratee (enumHandle h step))
(IO.hClose h)
:
:d text IO
|apidoc Data.Enumerator.Text.iterHandle|
iterHandle :: MonadIO m => IO.Handle
-> Iteratee T.Text m ()
iterHandle h = continue step where
step EOF = yield () EOF
step (Chunks []) = continue step
step (Chunks chunks) = do
tryIO (CM.mapM_ (TIO.hPutStr h) chunks)
continue step
: