module Simulation.Aivika.Experiment.Base.ExperimentWriter
(ExperimentWriter,
runExperimentWriter,
ExperimentFilePath(..),
experimentFilePath,
resolveFilePath,
expandFilePath,
mapFilePath) where
import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Control.Monad.State
import Control.Concurrent.MVar
import Control.Exception
import qualified Data.Map as M
import qualified Data.Set as S
import System.Directory
import System.FilePath
import Simulation.Aivika.Trans.Exception
import Simulation.Aivika.Experiment.Utils (replace)
data ExperimentFilePath = WritableFilePath FilePath
| UniqueFilePath FilePath
experimentFilePath :: ExperimentFilePath
experimentFilePath = UniqueFilePath "experiment"
resolveFilePath :: FilePath -> ExperimentFilePath -> ExperimentWriter FilePath
resolveFilePath dir (WritableFilePath path) =
return $ dir </> path
resolveFilePath dir (UniqueFilePath path) =
ExperimentWriter $ \r ->
let (name, ext) = splitExtension path
loop y i =
do let n = dir </> addExtension y ext
y' = name ++ "(" ++ show i ++ ")"
f1 <- doesFileExist n
f2 <- doesDirectoryExist n
if f1 || f2
then loop y' (i + 1)
else do n' <- liftIO $
modifyMVar r $ \s ->
if S.member n s
then return (s, Nothing)
else return (S.insert n s, Just n)
case n' of
Nothing -> loop y' (i + 1)
Just n' -> return n'
in loop name 2
expandFilePath :: ExperimentFilePath -> M.Map String String -> ExperimentFilePath
expandFilePath (WritableFilePath path) map = WritableFilePath (expandTemplates path map)
expandFilePath (UniqueFilePath path) map = UniqueFilePath (expandTemplates path map)
expandTemplates :: String -> M.Map String String -> String
expandTemplates name map = name' where
((), name') = flip runState name $
forM_ (M.assocs map) $ \(k, v) ->
do a <- get
put $ replace k v a
mapFilePath :: (FilePath -> FilePath) -> ExperimentFilePath -> ExperimentFilePath
mapFilePath f (WritableFilePath path) = WritableFilePath (f path)
mapFilePath f (UniqueFilePath path) = UniqueFilePath (f path)
newtype ExperimentWriter a = ExperimentWriter (MVar (S.Set String) -> IO a)
instance Functor ExperimentWriter where
fmap f (ExperimentWriter m) =
ExperimentWriter $ \r -> fmap f (m r)
instance Applicative ExperimentWriter where
pure a =
ExperimentWriter $ \r -> return a
(ExperimentWriter f) <*> (ExperimentWriter m) =
ExperimentWriter $ \r -> f r <*> m r
instance Monad ExperimentWriter where
return a =
ExperimentWriter $ \r -> return a
(ExperimentWriter m) >>= k =
ExperimentWriter $ \r ->
do a <- m r
let ExperimentWriter b = k a
b r
instance MonadIO ExperimentWriter where
liftIO m = ExperimentWriter $ \r -> liftIO m
instance MonadException ExperimentWriter where
catchComp (ExperimentWriter m) h =
ExperimentWriter $ \r ->
catch (m r) $ \e ->
let ExperimentWriter m' = h e in m' r
finallyComp (ExperimentWriter m) (ExperimentWriter m') =
ExperimentWriter $ \r ->
finally (m r) (m' r)
throwComp e =
ExperimentWriter $ \r ->
throw e
runExperimentWriter :: ExperimentWriter a -> IO a
runExperimentWriter (ExperimentWriter m) =
do r <- newMVar S.empty
m r