module Control.Monad.Coroutine.Iteratee
(
coroutineEnumerator, enumeratorCoroutine,
coroutineIteratee, iterateeCoroutine,
iterateeStreamCoroutine, streamCoroutineIteratee
)
where
import Control.Monad (liftM, unless)
import Control.Exception.Base (SomeException)
import Data.Monoid (Monoid, mconcat)
import Data.Iteratee.Base (Iteratee(..), Stream(..), icont, idone, idoneM, run)
import Data.Iteratee.Iteratee (Enumerator)
import Control.Monad.Coroutine
import Control.Monad.Coroutine.SuspensionFunctors (Await(Await), Yield(Yield), await, yield)
iterateeCoroutine :: (Monad m, Monoid s) => Iteratee s m b -> Coroutine (Await [s]) m (Either SomeException (b, [s]))
iterateeCoroutine = convert . iterateeStreamCoroutine
where convert = liftM streamChunk . mapSuspension convertSuspension
convertSuspension (Await cont) = Await (endOnEmptyChunk cont)
endOnEmptyChunk cont [] = cont (EOF Nothing)
endOnEmptyChunk cont xs = cont (Chunk $ mconcat xs)
streamChunk (Left e) = Left e
streamChunk (Right (b, EOF (Just e))) = Left e
streamChunk (Right (b, EOF Nothing)) = Right (b, [])
streamChunk (Right (b, Chunk chunk)) = Right (b, [chunk])
coroutineIteratee :: (Monad m, Monoid s) => Coroutine (Await [s]) m (Either SomeException (b, [s])) -> Iteratee s m b
coroutineIteratee = streamCoroutineIteratee . convert
. liftM (either Left (Right . \(b, chunk)-> (b, Chunk $ mconcat chunk)))
where convert cort = Coroutine {resume= resume cort >>= return . either (Left . convertSuspension) Right}
convertSuspension (Await cont) = Await (ignoreEmptyChunks (convert . cont))
ignoreEmptyChunks cont (Chunk chunk) = cont [chunk]
ignoreEmptyChunks cont (EOF e) = cont []
iterateeStreamCoroutine :: Monad m =>
Iteratee s m b -> Coroutine (Await (Stream s)) m (Either SomeException (b, Stream s))
iterateeStreamCoroutine iter = Coroutine{resume= runIter iter convertDone convertContinue}
where convertDone b s = return (Right $ Right (b, s))
convertContinue cont (Just e) = return (Right $ Left e)
convertContinue cont Nothing = return (Left $ Await (iterateeStreamCoroutine . cont))
streamCoroutineIteratee :: Monad m =>
Coroutine (Await (Stream s)) m (Either SomeException (b, Stream s)) -> Iteratee s m b
streamCoroutineIteratee c = Iteratee (\done continue-> resume c >>= convertStep done continue)
where convertStep done continue (Left (Await cont)) = continue (streamCoroutineIteratee . cont) Nothing
convertStep done continue (Right (Left e)) =
continue (const $ streamCoroutineIteratee $ return (Left e)) (Just e)
convertStep done continue (Right (Right (b, s))) = done b s
enumeratorCoroutine :: Monad m => Enumerator s (Coroutine (Yield [s]) m) () -> Coroutine (Yield [s]) m ()
enumeratorCoroutine enum = enum (icont step Nothing) >> return ()
where step (Chunk c) = Iteratee (\done continue-> yield [c] >> continue step Nothing)
step s@EOF{} = idone () s
coroutineEnumerator :: (Monad m, Monoid s) => Coroutine (Yield [s]) m b -> Enumerator s m c
coroutineEnumerator c i = runIter i idoneM continue
where feed step (Yield chunk c) = coroutineEnumerator c (step (Chunk $ mconcat chunk))
continue step Nothing = resume c >>= either (feed step) (const $ return $ step (EOF Nothing))