{-# LANGUAGE NoImplicitPrelude #-}
module Data.Machine.Zlib where

import BasicPrelude
import Control.Exception (throw)
import Data.Machine
import Data.Streaming.Zlib

-- | Even though this doesn't actually do any IO, we stay in the IO monad
-- like Data.Streaming.Zlib does, to avoid any unsafe "black magic".
gunzipper :: (Functor m, MonadIO m) => ProcessT m ByteString ByteString
gunzipper = construct $ do
  inflate <- liftIO . initInflate $ WindowBits 31
  let go :: (Functor m, MonadIO m) => PlanT (Is ByteString) ByteString m ()
      go = do
        bs <- await <|> finish
        popper <- liftIO <$> liftIO (feedInflate inflate bs)
        exhaust (handleRes <$> popper) <|> go
      handleRes PRDone = Nothing
      handleRes (PRNext bs) = Just bs
      handleRes (PRError e) = throw e
      finish :: MonadIO m => PlanT (Is ByteString) ByteString m ByteString
      finish = do
        r <- liftIO $ finishInflate inflate
        yield r
        stop
  go