module Control.Monad.Coroutine.Iteratee
(
coroutineEnumerator, enumeratorCoroutine,
coroutineIteratee, iterateeCoroutine,
iterateeStreamCoroutine, streamCoroutineIteratee
)
where
import Control.Monad (liftM)
import Control.Exception.Base (SomeException)
import Data.Monoid (Monoid, mconcat)
import Data.Iteratee.Base (Iteratee(..), Stream(..), icont, idone, idoneM)
import Data.Iteratee.Iteratee (Enumerator)
import Control.Monad.Coroutine
import Control.Monad.Coroutine.SuspensionFunctors (Await(Await), Yield(Yield), 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 (_, 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{} = 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 _ (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 _ continue (Left (Await cont)) = continue (streamCoroutineIteratee . cont) Nothing
convertStep _ continue (Right (Left e)) = continue (const $ streamCoroutineIteratee $ return (Left e)) (Just e)
convertStep done _ (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 (\_ 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))
continue step e = return (icont step e)