module Data.Conduit.Util.Source
    ( sourceState
    , sourceStateIO
    , SourceStateResult (..)
    , sourceIO
    , SourceIOResult (..)
    ) where
import Control.Monad.Trans.Resource
import Data.Conduit.Internal
data SourceStateResult state output = StateOpen state output | StateClosed
sourceState
    :: Monad m
    => state 
    -> (state -> m (SourceStateResult state output)) 
    -> Source m output
sourceState state0 pull0 =
    src state0
  where
    src state = PipeM (pull state)
    pull state = do
        res <- pull0 state
        return $ case res of
            StateOpen state' val -> HaveOutput (src state') (return ()) val
            StateClosed -> Done ()
data SourceIOResult output = IOOpen output | IOClosed
sourceIO :: MonadResource m
          => IO state 
          -> (state -> IO ()) 
          -> (state -> m (SourceIOResult output)) 
          -> Source m output
sourceIO alloc cleanup pull0 =
    PipeM (do
        (key, state) <- allocate alloc cleanup
        pull key state)
  where
    src key state = PipeM (pull key state)
    pull key state = do
        res <- pull0 state
        case res of
            IOClosed -> do
                release key
                return $ Done ()
            IOOpen val -> return $ HaveOutput (src key state) (release key) val
sourceStateIO :: MonadResource m
              => IO state 
              -> (state -> IO ()) 
              -> (state -> m (SourceStateResult state output)) 
              -> Source m output
sourceStateIO alloc cleanup pull0 =
    PipeM (do
        (key, state) <- allocate alloc cleanup
        pull key state)
  where
    src key state = PipeM (pull key state)
    pull key state = do
        res <- pull0 state
        case res of
            StateClosed -> do
                release key
                return $ Done ()
            StateOpen state' val -> return $ HaveOutput (src key state') (release key) val