omnicodec-0.5.0.2: src/Data/ByteString/Iteratee/Internals.hs
-- |
-- Module : Data.ByteString.Iteratee.Internals
-- Copyright : (c) Magnus Therning 2009
-- License : BSD
-- Maintainer : Magnus Therning <magnus@therning.org>
module Data.ByteString.Iteratee.Internals
where
import Data.ByteString as BS
import Control.Monad.Trans
-- import Control.Monad.IO.Class
-- | A stream is a sequence of elements, presented in @Chunk@s. The stream,
-- very predictably, ends with @Eof@.
data Stream
= Eof
| Chunk ByteString
-- | A stepper is the result type of an iteratee as it's folded over a 'Stream'.
data Stepper m a
= Done a Stream
| NeedAnotherChunk (Iteratee m a)
-- | An iteratee is the thing that's folded over a 'Stream'. All iteratees are
-- expected to move to the 'Done' state when they receive 'Eof'.
newtype Iteratee m a = Iteratee
{ runIteratee :: Stream -> m (Stepper m a) }
-- | An enumerator is an iteratee transformer. It passes a sequence of
-- 'Stream' to the iteratee, ending with 'Eof'. It stops when the iteratee
-- reaches the 'Done' state.
type Enumerator m a = Iteratee m a -> m (Iteratee m a)
-- | An enumeratee is both an iteratee and an enumerator. It accepts a
-- sequence of 'Stream', modifies the data, and then passes it on to the inner
-- iteratee.
type Enumeratee m a = Iteratee m a -> Iteratee m (Iteratee m a)
instance (Monad m) => Monad (Iteratee m) where
return x = Iteratee $ \ s -> return $ Done x s
i >>= f = Iteratee $ \ s -> do
step <- runIteratee i s
case step of
Done v s' -> runIteratee (f v) s'
NeedAnotherChunk i' -> return . NeedAnotherChunk $ i' >>= f
instance (MonadIO m) => MonadIO (Iteratee m) where
liftIO a = Iteratee $ \ s -> do
r <- liftIO a
return $ Done r s
-- | The most basic of enumerators, it applies the iteratee to the terminated
-- stream.
enumEof :: (Monad m) => Enumerator m a
enumEof iter = do
step <- runIteratee iter Eof
case step of
Done x _ -> return $ Iteratee $ return . Done x
NeedAnotherChunk i -> return i
-- | An enumerator which passes the given string in a single 'Chunk'.
enumPure1Chunk :: Monad m => ByteString -> Enumerator m a
enumPure1Chunk bs iter = run bs iter
where
run bs' i
| BS.null bs' = runIteratee i Eof >>= checkIfDone return
| otherwise = runIteratee i (Chunk bs') >>= checkIfDone (run empty)
-- | An enumerator which passes the given string, chopped up into the given
-- length, in a stream of 'Chunk's.
enumPureManyChunk :: Monad m => ByteString -> Int -> Enumerator m a
enumPureManyChunk bs n iter = run bs n iter
where
run bs' n' i
| BS.null bs' = runIteratee i Eof >>= checkIfDone return
| n' > 0 = let
(p1, p2) = BS.splitAt n' bs'
in runIteratee i (Chunk p1) >>= checkIfDone (run p2 n')
| otherwise = error $ "enumPureManyChunk: called with n'=" ++ show n'
-- | Useful function for defining enumerators. See source for 'enumPure1Chunk'
-- for example of usage.
checkIfDone :: Monad m =>
(Iteratee m a -> m (Iteratee m a)) -- ^ Function applied to the iteratee, if next argument is 'NeedAnotherChunk'
-> Stepper m a
-> m (Iteratee m a)
checkIfDone _ (Done x _) = return $ return x
checkIfDone f (NeedAnotherChunk i') = f i'