module Potoki.Core.Produce
(
Produce(..),
list,
transform,
vector,
hashMapRows,
fileBytes,
fileBytesAtOffset,
fileText,
stdinBytes,
directoryContents,
finiteMVar,
infiniteMVar,
lazyByteString,
)
where
import Potoki.Core.Prelude
import Potoki.Core.Types
import qualified Potoki.Core.Fetch as A
import qualified Data.HashMap.Strict as B
import qualified Data.Vector as C
import qualified System.Directory as G
import qualified Acquire.Acquire as M
import qualified Data.ByteString.Lazy as D
deriving instance Functor Produce
instance Applicative Produce where
pure x = Produce $ do
refX <- liftIO (newIORef (Just x))
return (A.maybeRef refX)
(<*>) (Produce leftAcquire) (Produce rightAcquire) =
Produce ((<*>) <$> leftAcquire <*> rightAcquire)
instance Alternative Produce where
empty =
Produce (pure empty)
(<|>) (Produce leftAcquire) (Produce rightAcquire) =
Produce ((<|>) <$> leftAcquire <*> rightAcquire)
instance Monad Produce where
return = pure
(>>=) (Produce (Acquire io1)) k2 =
Produce $ Acquire $ do
(A.Fetch fetch1, release1) <- io1
release2Ref <- newIORef (return ())
fetch3Var <- newIORef (return Nothing)
let
fetch2 input1 =
case k2 input1 of
Produce (Acquire io2) -> do
join (readIORef release2Ref)
(A.Fetch fetch2', release2') <- io2
writeIORef release2Ref release2'
return fetch2'
release3 =
join (readIORef release2Ref) >> release1
fetch3 = do
res <- readIORef fetch3Var
mayY <- res
case mayY of
Nothing -> do
mayX <- fetch1
case mayX of
Nothing -> return Nothing
Just x -> do
fetch2 x >>= writeIORef fetch3Var
fetch3
Just y -> return $ Just y
return (A.Fetch fetch3, release3)
instance MonadIO Produce where
liftIO io = Produce . liftIO $ do
refX <- newIORef $ Just io
let fetch = A.Fetch $ fetchIO refX
where
fetchIO ref = do
elemVal <- readIORef ref
for elemVal $ \getElement -> do
writeIORef ref Nothing
getElement
return fetch
{-# INLINABLE list #-}
list :: [input] -> Produce input
list inputList =
Produce $ liftIO (A.list <$> newIORef inputList)
{-# INLINE transform #-}
transform :: Transform input output -> Produce input -> Produce output
transform (Transform transformAcquire) (Produce produceAcquire) =
Produce $ do
fetch <- produceAcquire
transformAcquire fetch
{-# INLINE vector #-}
vector :: Vector input -> Produce input
vector vectorVal =
Produce $ M.Acquire $ do
indexRef <- newIORef 0
let
fetch =
A.Fetch $ do
indexVal <- readIORef indexRef
writeIORef indexRef $! succ indexVal
return $ (C.!?) vectorVal indexVal
in return (fetch, return ())
{-# INLINE hashMapRows #-}
hashMapRows :: HashMap a b -> Produce (a, b)
hashMapRows =
list . B.toList
{-# INLINABLE fileBytes #-}
fileBytes :: FilePath -> Produce (Either IOException ByteString)
fileBytes path =
accessingHandle (openBinaryFile path ReadMode) A.handleBytes
{-# INLINABLE fileBytesAtOffset #-}
fileBytesAtOffset :: FilePath -> Int -> Produce (Either IOException ByteString)
fileBytesAtOffset path offset =
accessingHandle acquire A.handleBytes
where
acquire =
do
handleVal <- openBinaryFile path ReadMode
hSeek handleVal AbsoluteSeek (fromIntegral offset)
return handleVal
{-# INLINABLE accessingHandle #-}
accessingHandle :: IO Handle -> (Handle -> A.Fetch (Either IOException a)) -> Produce (Either IOException a)
accessingHandle acquireHandle fetch =
Produce $ M.Acquire (catchIOError normal failing)
where
normal =
do
handleVal <- acquireHandle
return (fetch handleVal, catchIOError (hClose handleVal) (const (return ())))
failing exception =
return (pure (Left exception), return ())
{-# INLINABLE stdinBytes #-}
stdinBytes :: Produce (Either IOException ByteString)
stdinBytes =
Produce $ M.Acquire (return (A.handleBytes stdin, return ()))
{-# INLINABLE directoryContents #-}
directoryContents :: FilePath -> Produce (Either IOException FilePath)
directoryContents path =
Produce $ M.Acquire (catchIOError success failure)
where
success =
do
subPaths <- G.listDirectory path
ref <- newIORef (map (Right . mappend path . (:) '/') (sort subPaths))
return (A.list ref, return ())
failure exception =
return (pure (Left exception), return ())
{-# INLINABLE fileText #-}
fileText :: FilePath -> Produce (Either IOException Text)
fileText path =
Produce $ M.Acquire (catchIOError success failure)
where
success =
do
handleVal <- openFile path ReadMode
return (A.handleText handleVal, catchIOError (hClose handleVal) (const (return ())))
failure exception =
return (pure (Left exception), return ())
{-# INLINE finiteMVar #-}
finiteMVar :: MVar (Maybe element) -> Produce element
finiteMVar var =
Produce $ M.Acquire (return (A.finiteMVar var, return ()))
{-# INLINE infiniteMVar #-}
infiniteMVar :: MVar element -> Produce element
infiniteMVar var =
Produce $ M.Acquire (return (A.infiniteMVar var, return ()))
{-# INLINE lazyByteString #-}
lazyByteString :: D.ByteString -> Produce ByteString
lazyByteString lbs =
Produce $ M.Acquire $ do
ref <- newIORef lbs
return (A.lazyByteStringRef ref, return ())