fs-sim-0.3.0.0: src/System/FS/Sim/STM.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | 'HasFS' instance using 'MockFS' stored in an STM variable
module System.FS.Sim.STM (
runSimFS
, simHasFS
, simHasFS'
) where
import Control.Concurrent.Class.MonadSTM.Strict
import Control.Monad.Class.MonadThrow
import Control.Monad.Primitive
import System.FS.API
import qualified System.FS.Sim.MockFS as Mock
import System.FS.Sim.MockFS (HandleMock, MockFS)
import System.FS.Sim.Prim
{------------------------------------------------------------------------------
The simulation-related types
------------------------------------------------------------------------------}
--- | Runs a computation provided an initial 'MockFS', producing a
--- result, the final state of the filesystem and a sequence of actions occurred
--- in the filesystem.
runSimFS :: (MonadSTM m, MonadThrow m, PrimMonad m)
=> MockFS
-> (HasFS m HandleMock -> m a)
-> m (a, MockFS)
runSimFS fs act = do
var <- newTMVarIO fs
a <- act (simHasFS var)
fs' <- atomically $ takeTMVar var
return (a, fs')
-- | Alternative to 'simHasFS' that creates 'TVar's internally.
simHasFS' :: (MonadSTM m, MonadThrow m, PrimMonad m)
=> MockFS
-> m (HasFS m HandleMock)
simHasFS' mockFS = simHasFS <$> newTMVarIO mockFS
-- | Equip @m@ with a @HasFs@ instance using the mock file system
simHasFS :: forall m. (MonadSTM m, MonadThrow m, PrimMonad m)
=> StrictTMVar m MockFS
-> HasFS m HandleMock
simHasFS var = HasFS {
dumpState = sim Mock.dumpState
, hOpen = sim .: Mock.hOpen
, hClose = sim . Mock.hClose
, hIsOpen = sim . Mock.hIsOpen
, hSeek = sim ..: Mock.hSeek
, hGetSome = sim .: Mock.hGetSome
, hGetSomeAt = sim ..: Mock.hGetSomeAt
, hPutSome = sim .: Mock.hPutSome
, hTruncate = sim .: Mock.hTruncate
, hGetSize = sim . Mock.hGetSize
, createDirectory = sim . Mock.createDirectory
, createDirectoryIfMissing = sim .: Mock.createDirectoryIfMissing
, listDirectory = sim . Mock.listDirectory
, doesDirectoryExist = sim . Mock.doesDirectoryExist
, doesFileExist = sim . Mock.doesFileExist
, removeDirectoryRecursive = sim . Mock.removeDirectoryRecursive
, removeFile = sim . Mock.removeFile
, renameFile = sim .: Mock.renameFile
, mkFsErrorPath = fsToFsErrorPathUnmounted
, unsafeToFilePath = \_ -> error "simHasFS:unsafeToFilePath"
-- File I\/O with user-supplied buffers
, hGetBufSome = sim ...: Mock.hGetBufSome
, hGetBufSomeAt = sim ....: Mock.hGetBufSomeAt
, hPutBufSome = sim ...: Mock.hPutBufSome
, hPutBufSomeAt = sim ....: Mock.hPutBufSomeAt
}
where
sim :: FSSimT m a -> m a
sim m = do
st <- atomically $ takeTMVar var
runFSSimT m st >>= \case
Left e -> do
atomically $ putTMVar var st
throwIO e
Right (a, st') -> do
atomically $ putTMVar var st'
pure a
(.:) :: (y -> z) -> (x0 -> x1 -> y) -> (x0 -> x1 -> z)
(f .: g) x0 x1 = f (g x0 x1)
(..:) :: (y -> z) -> (x0 -> x1 -> x2 -> y) -> (x0 -> x1 -> x2 -> z)
(f ..: g) x0 x1 x2 = f (g x0 x1 x2)
(...:) :: (y -> z) -> (x0 -> x1 -> x2 -> x3 -> y) -> (x0 -> x1 -> x2 -> x3 -> z)
(f ...: g) x0 x1 x2 x3 = f (g x0 x1 x2 x3)
(....:) :: (y -> z) -> (x0 -> x1 -> x2 -> x3 -> x4 -> y) -> (x0 -> x1 -> x2 -> x3 -> x4 -> z)
(f ....: g) x0 x1 x2 x3 x4 = f (g x0 x1 x2 x3 x4)