-- | A light-weight wrapper around @Network.Wai@ to provide easy conduit support.
module Pipes.Wai
    ( -- * Request body
      producerRequestBody
      -- * Response body
    , responseProducer
    , responseRawProducer
      -- * Re-export
    , module Network.Wai
    ) where

import Network.Wai
import Pipes
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.ByteString (ByteString)
import qualified Data.ByteString as S
import Control.Monad (unless)
import Network.HTTP.Types
import Blaze.ByteString.Builder (Builder)
import Data.IORef
import qualified Pipes.Prelude as CL

data Flush a = Chunk a | Flush
             deriving (Eq, Ord, Show)

instance Functor Flush where
  fmap f c = case c of
    Chunk a -> Chunk $ f a
    Flush -> Flush

-- | Stream the request body.
--
-- Since 3.0.0
producerRequestBody :: MonadIO m => Request -> Producer ByteString m ()
producerRequestBody req =
    loop
  where
    go = liftIO (requestBody req)

    loop = do
        bs <- go
        unless (S.null bs) $ do
            yield bs
            loop

-- | Create an HTTP response out of a @Producer@.
--
-- Since 3.0.0
responseProducer :: Status -> ResponseHeaders -> Producer (Flush Builder) IO () -> Response
responseProducer s hs src = responseStream s hs $ \send flush ->
  runEffect $ for src $ \mbuilder -> case mbuilder of
    Chunk b -> lift $ send b
    Flush -> lift $ flush

-- | Create a raw response using a @Source@ and @Sink@ to represent the input
-- and output, respectively.
--
-- Since 3.0.0
responseRawProducer :: (MonadIO m, MonadIO n)
                    => (Producer ByteString m () -> Consumer ByteString n () -> IO ())
                    -> Response
                    -> Response
responseRawProducer app = responseRaw app'
  where
    app' recv send =
        app src sink
      where
        src = do
          bs <- liftIO recv
          unless (S.null bs) $ do
            yield bs
            src
        sink = (await >>= liftIO . send) >> sink