module Control.Monad.Coroutine.Enumerator
(
coroutineEnumerator, enumeratorCoroutine,
coroutineIteratee, iterateeCoroutine,
iterateeStreamCoroutine, streamCoroutineIteratee
)
where
import Control.Monad (liftM, unless)
import Control.Exception.Base (SomeException)
import Data.Enumerator (Enumerator, Iteratee(..), Stream(..))
import qualified Data.Enumerator as Enumerator
import Control.Monad.Coroutine
import Control.Monad.Coroutine.SuspensionFunctors (Await(Await), Yield(Yield), yield)
iterateeCoroutine :: Monad m => Iteratee a m b -> Coroutine (Await [a]) m (Either SomeException (b, [a]))
iterateeCoroutine = convert . iterateeStreamCoroutine
where convert = liftM (either Left (Right . streamChunk)) . mapSuspension convertSuspension
convertSuspension (Await cont) = Await (endOnEmptyChunk cont)
endOnEmptyChunk cont [] = cont EOF
endOnEmptyChunk cont chunk = cont (Chunks chunk)
streamChunk (b, EOF) = (b, [])
streamChunk (b, Chunks chunk) = (b, chunk)
coroutineIteratee :: Monad m => Coroutine (Await [a]) m (Either SomeException (b, [a])) -> Iteratee a m b
coroutineIteratee = streamCoroutineIteratee . convert . liftM (either Left (Right . \(b, chunk)-> (b, Chunks chunk)))
where convert cort = Coroutine {resume= resume cort >>= return . either (Left . convertSuspension) Right}
convertSuspension (Await cont) = Await (ignoreEmptyChunks (convert . cont))
ignoreEmptyChunks cont (Chunks []) = suspend $ Await (ignoreEmptyChunks cont)
ignoreEmptyChunks cont (Chunks chunk) = cont chunk
ignoreEmptyChunks cont EOF = cont []
iterateeStreamCoroutine :: Monad m =>
Iteratee a m b -> Coroutine (Await (Stream a)) m (Either SomeException (b, Stream a))
iterateeStreamCoroutine iter = Coroutine{resume= liftM convertStep $ runIteratee iter}
where convertStep (Enumerator.Yield b s) = Right $ Right (b, s)
convertStep (Enumerator.Error e) = Right $ Left e
convertStep (Enumerator.Continue cont) = Left (Await (iterateeStreamCoroutine . cont))
streamCoroutineIteratee :: Monad m
=> Coroutine (Await (Stream a)) m (Either SomeException (b, Stream a)) -> Iteratee a m b
streamCoroutineIteratee c = Iteratee (liftM convertStep $ resume c)
where convertStep (Left (Await cont)) = Enumerator.Continue (streamCoroutineIteratee . cont)
convertStep (Right (Left e)) = Enumerator.Error e
convertStep (Right (Right (b, s))) = Enumerator.Yield b s
enumeratorCoroutine :: Monad m => Enumerator a (Coroutine (Yield [a]) m) () -> Coroutine (Yield [a]) m ()
enumeratorCoroutine enum = runIteratee (enum step) >> return ()
where step = Enumerator.Continue yielding
yielding (Chunks xs) = Iteratee (unless (null xs) (yield xs) >> return step)
yielding EOF = Enumerator.returnI (Enumerator.Yield () EOF)
coroutineEnumerator :: Monad m => Coroutine (Yield [a]) m b -> Enumerator a m c
coroutineEnumerator c step@(Enumerator.Continue cont) = Iteratee (resume c >>= either feed (const $ return step))
where feed (Yield chunk c') = runIteratee (cont (Chunks chunk)) >>= runIteratee . coroutineEnumerator c'
coroutineEnumerator _ step = Enumerator.returnI step