{- Copyright 2010 Mario Blazevic This file is part of the Streaming Component Combinators (SCC) project. The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SCC. If not, see . -} -- | This module provides a bridge between the Control.Monad.Coroutine and the Data.Enumerator monad transformers. 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) -- | Converts an 'Iteratee' into a 'Coroutine' parameterized with the 'Await' [x] functor. The coroutine treats an empty -- input chunk as 'EOF'. 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) -- | Converts a 'Coroutine' parameterized with the 'Await' [x] functor, treating an empty input chunk as 'EOF', into an -- 'Iteratee'. 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 [] -- | Converts an 'Iteratee' into a 'Coroutine' parameterized with the 'Await' ('Stream' x) functor. 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)) -- | Converts a 'Coroutine' parameterized with the 'Await' functor into an 'Iteratee'. 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 -- | Converts an 'Enumerator' into a 'Coroutine' parameterized with the 'Yield' functor. 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) -- | Converts a 'Coroutine' parameterized with the 'Yield' functor into an 'Enumerator'. 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