{-# LANGUAGE FlexibleContexts #-}
-- | Defines the types for a source, which is a producer of data.
module Data.Conduit.Types.Source
( SourceResult (..)
, Source (..)
) where
import Control.Monad.Trans.Resource
import Data.Monoid (Monoid (..))
import Control.Monad (liftM)
-- | Result of pulling from a source. Either a new piece of data (@Open@), or
-- indicates that the source is now @Closed@.
--
-- The @Open@ constructor returns both a new value, as well as a new @Source@,
-- which should be used in place of the previous @Source@.
--
-- Since 0.2.0
data SourceResult m a = Open (Source m a) a | Closed
instance Monad m => Functor (SourceResult m) where
fmap f (Open p a) = Open (fmap f p) (f a)
fmap _ Closed = Closed
-- | A @Source@ has two operations on it: pull some data, and close the
-- @Source@. Since @Source@ is built on top of 'ResourceT', all acquired
-- resources should be automatically released anyway. Closing a @Source@ early
-- is merely an optimization to free scarce resources as soon as possible.
--
-- A @Source@ is should free any resources it allocated when either
-- @sourceClose@ is called or a @Closed@ is returned. However, based on the
-- usage of @ResourceT@, this is simply an optimization.
--
-- Since 0.2.0
data Source m a = Source
{ sourcePull :: ResourceT m (SourceResult m a)
, sourceClose :: ResourceT m ()
}
instance Monad m => Functor (Source m) where
fmap f src = src
{ sourcePull = liftM (fmap f) (sourcePull src)
}
instance Resource m => Monoid (Source m a) where
mempty = Source
{ sourcePull = return Closed
, sourceClose = return ()
}
mappend a b = mconcat [a, b]
mconcat [] = mempty
mconcat (next0:rest0) =
src next0 rest0
where
src next rest = Source (pull next rest) (close next rest)
pull current rest = do
res <- sourcePull current
case res of
-- end of the current Source
Closed -> do
case rest of
-- ... and open the next one
a:as -> pull a as
-- no more source, return an EOF
[] -> return Closed
Open current' val -> return (Open (src current' rest) val)
close current _rest = do
-- we only need to close the current Source, since they are opened
-- one at a time
sourceClose current