import System.Random
import Data.Word
+import Text.Read
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import qualified Data.ByteString as B
import qualified Utility.RawFilePath as R
import qualified System.FilePath.ByteString as P
-data SimState = SimState
+data SimState t = SimState
{ simRepos :: M.Map RepoName UUID
- , simRepoList :: [RepoName]
- , simRepoState :: M.Map UUID SimRepoState
+ , simRepoState :: M.Map UUID (SimRepoState t)
, simConnections :: M.Map UUID (S.Set RemoteName)
, simFiles :: M.Map RawFilePath Key
- , simRng :: StdGen
+ , simRng :: Int
, simTrustLevels :: M.Map UUID TrustLevel
, simNumCopies :: NumCopies
, simMinCopies :: MinCopies
, simGroupWanted :: M.Map Group PreferredContentExpression
, simMaxSize :: M.Map UUID MaxSize
, simRebalance :: Bool
- , simGetExistingRepoByName :: GetExistingRepoByName
- , simGetSimRepoPath :: GetSimRepoPath
, simHistory :: [SimCommand]
, simVectorClock :: VectorClock
+ , simFile :: Maybe FilePath
+ , simRootDirectory :: FilePath
}
- deriving (Show)
+ deriving (Show, Read)
-emptySimState :: StdGen -> GetExistingRepoByName -> GetSimRepoPath -> SimState
-emptySimState rng repobyname getpath = SimState
+emptySimState :: Int -> FilePath -> SimState t
+emptySimState rngseed rootdir = SimState
{ simRepos = mempty
- , simRepoList = mempty
, simRepoState = mempty
, simConnections = mempty
, simFiles = mempty
- , simRng = rng
+ , simRng = rngseed
, simTrustLevels = mempty
, simNumCopies = configuredNumCopies 1
, simMinCopies = configuredMinCopies 1
, simGroupWanted = mempty
, simMaxSize = mempty
, simRebalance = False
- , simGetExistingRepoByName = repobyname
- , simGetSimRepoPath = getpath
, simHistory = []
, simVectorClock = VectorClock 0
+ , simFile = Nothing
+ , simRootDirectory = rootdir
}
-- State that can vary between different repos in the simulation.
-data SimRepoState = SimRepoState
+data SimRepoState t = SimRepoState
{ simLocations :: M.Map Key (M.Map UUID LocationState)
, simIsSpecialRemote :: Bool
- , simRepo :: Maybe SimRepo
+ , simRepo :: Maybe t
, simRepoName :: RepoName
}
- deriving (Show)
+ deriving (Show, Read)
data LocationState = LocationState VectorClock Bool
- deriving (Eq, Show)
+ deriving (Eq, Show, Read)
newtype VectorClock = VectorClock Int
- deriving (Eq, Ord, Show)
+ deriving (Eq, Ord, Show, Read)
newerLocationState :: LocationState -> LocationState -> LocationState
newerLocationState l1@(LocationState vc1 _) l2@(LocationState vc2 _)
| otherwise = l2
{- Updates the state of stu to indicate that a key is present or not in u. -}
-setPresentKey :: Bool -> UUID -> Key -> UUID -> SimState -> SimState
+setPresentKey :: Bool -> UUID -> Key -> UUID -> SimState SimRepo -> SimState SimRepo
setPresentKey present u k stu st = st
{ simRepoState = case M.lookup stu (simRepoState st) of
Just rst -> M.insert stu
Nothing -> error "no simRepoState in setPresentKey"
}
-setPresentKey' :: Bool -> VectorClock -> UUID -> Key -> SimRepoState -> SimRepoState
+setPresentKey' :: Bool -> VectorClock -> UUID -> Key -> SimRepoState t -> SimRepoState t
setPresentKey' present vc u k rst = rst
{ simLocations =
M.insertWith (M.unionWith newerLocationState) k
(simLocations rst)
}
-getSimLocations :: SimRepoState -> Key -> S.Set UUID
+getSimLocations :: SimRepoState t -> Key -> S.Set UUID
getSimLocations rst k =
maybe mempty getSimLocations' $
M.lookup k (simLocations rst)
where
present (LocationState _ b) = b
-addHistory :: SimState -> SimCommand -> SimState
+addHistory :: SimState t -> SimCommand -> SimState t
addHistory st c = st { simHistory = c : simHistory st }
newtype RepoName = RepoName { fromRepoName :: String }
- deriving (Show, Eq, Ord)
+ deriving (Show, Read, Eq, Ord)
newtype RemoteName = RemoteName { fromRemoteName :: String }
- deriving (Show, Eq, Ord)
+ deriving (Show, Read, Eq, Ord)
remoteNameToRepoName :: RemoteName -> RepoName
remoteNameToRepoName (RemoteName n) = RepoName n
| RepoName :=> Connections
| RemoteName :<= Connections
| RepoName :<=> Connections
- deriving (Show)
+ deriving (Show, Read)
leftSideOfConnection :: Connections -> RepoName
leftSideOfConnection (reponame :-> _) = reponame
| CommandRebalance Bool
| CommandComment String
| CommandBlank
- deriving (Show)
+ deriving (Show, Read)
data SimAction
= ActionPull RemoteName
| ActionSendWanted RemoteName
| ActionGitPush RemoteName
| ActionGitPull RemoteName
- deriving (Show)
+ deriving (Show, Read)
-runSimCommand :: SimCommand -> SimState -> Annex SimState
-runSimCommand (CommandStep n) st
+runSimCommand :: SimCommand -> GetExistingRepoByName -> SimState SimRepo -> Annex (SimState SimRepo)
+runSimCommand (CommandStep n) repobyname st
| n > 0 = case randomRepo st of
(Just (repo, u), st') ->
let (act, st'') = randomAction u st'
- in runSimCommand (CommandAction repo act) st''
- >>= runSimCommand (CommandStep (pred n))
+ in runSimCommand (CommandAction repo act) repobyname st''
+ >>= runSimCommand (CommandStep (pred n)) repobyname
(Nothing, st') -> return st'
| otherwise = return st
-runSimCommand cmd st = case applySimCommand cmd st of
- Left err -> giveup err
- Right (Right st') -> return st'
- Right (Left mkst) -> mkst
+runSimCommand cmd repobyname st =
+ case applySimCommand cmd st repobyname of
+ Left err -> giveup err
+ Right (Right st') -> return st'
+ Right (Left mkst) -> mkst
applySimCommand
:: SimCommand
- -> SimState
- -> Either String (Either (Annex SimState) SimState)
+ -> SimState SimRepo
+ -> GetExistingRepoByName
+ -> Either String (Either (Annex (SimState SimRepo)) (SimState SimRepo))
applySimCommand cmd st =
applySimCommand' cmd $ flip addHistory cmd $ st
{ simVectorClock =
applySimCommand'
:: SimCommand
- -> SimState
- -> Either String (Either (Annex SimState) SimState)
-applySimCommand' (CommandInit reponame) st =
+ -> SimState SimRepo
+ -> GetExistingRepoByName
+ -> Either String (Either (Annex (SimState SimRepo)) (SimState SimRepo))
+applySimCommand' (CommandInit reponame) st _ =
checkNonexistantRepo reponame st $
let (u, st') = genSimUUID st reponame
in Right $ Right $ addRepo reponame (newSimRepoConfig u False) st'
-applySimCommand' (CommandInitRemote reponame) st =
+applySimCommand' (CommandInitRemote reponame) st _ =
checkNonexistantRepo reponame st $
let (u, st') = genSimUUID st reponame
in Right $ Right $ addRepo reponame (newSimRepoConfig u True) st'
-applySimCommand' (CommandUse reponame s) st =
- case getExistingRepoByName (simGetExistingRepoByName st) s of
+applySimCommand' (CommandUse reponame s) st repobyname =
+ case getExistingRepoByName repobyname s of
Right existingrepo -> checkNonexistantRepo reponame st $
Right $ Right $ addRepo reponame existingrepo st
Left msg -> Left $ "Unable to use a repository \""
++ fromRepoName reponame
++ "\" in the simulation because " ++ msg
-applySimCommand' (CommandConnect connections) st =
+applySimCommand' (CommandConnect connections) st repobyname =
let (repo, remote, mconnections) = getConnection connections
in checkKnownRepo repo st $ \u ->
let st' = st
in case mconnections of
Nothing -> Right $ Right st'
Just connections' ->
- applySimCommand' (CommandConnect connections') st'
-applySimCommand' (CommandDisconnect connections) st =
+ applySimCommand' (CommandConnect connections') st' repobyname
+applySimCommand' (CommandDisconnect connections) st repobyname =
let (repo, remote, mconnections) = getConnection connections
in checkKnownRepo repo st $ \u ->
let st' = st
in case mconnections of
Nothing -> Right $ Right $ st
Just connections' ->
- applySimCommand' (CommandDisconnect connections') st'
-applySimCommand' (CommandAddTree repo expr) st =
+ applySimCommand' (CommandDisconnect connections') st' repobyname
+applySimCommand' (CommandAddTree repo expr) st _ =
checkKnownRepo repo st $ const $
checkValidPreferredContentExpression expr $ Left $
error "TODO" -- XXX
-applySimCommand' (CommandAdd file sz repos) st =
+applySimCommand' (CommandAdd file sz repos) st _ =
let (k, st') = genSimKey sz st
in go k st' repos
where
{ simFiles = M.insert file k (simFiles st')
}
in go k st'' rest
-applySimCommand' (CommandStep _) _ = error "applySimCommand' CommandStep"
-applySimCommand' (CommandAction repo act) st =
+applySimCommand' (CommandStep _) _ _ = error "applySimCommand' CommandStep"
+applySimCommand' (CommandAction repo act) st _ =
checkKnownRepo repo st $ \u ->
applySimAction repo u act st
-applySimCommand' (CommandSeed rngseed) st = Right $ Right $ st
- { simRng = mkStdGen rngseed
- }
-applySimCommand' (CommandPresent repo file) st = checkKnownRepo repo st $ \u ->
+applySimCommand' (CommandSeed rngseed) st _ =
+ Right $ Right $ st
+ { simRng = rngseed
+ }
+applySimCommand' (CommandPresent repo file) st _ = checkKnownRepo repo st $ \u ->
case (M.lookup file (simFiles st), M.lookup u (simRepoState st)) of
(Just k, Just rst)
| u `S.member` getSimLocations rst k ->
missing = Left $ "Expected " ++ fromRawFilePath file
++ " to be present in "
++ fromRepoName repo ++ ", but it is not."
-applySimCommand' (CommandNotPresent repo file) st = checkKnownRepo repo st $ \u ->
+applySimCommand' (CommandNotPresent repo file) st _ = checkKnownRepo repo st $ \u ->
case (M.lookup file (simFiles st), M.lookup u (simRepoState st)) of
(Just k, Just rst)
| u `S.notMember` getSimLocations rst k ->
present = Left $ "Expected " ++ fromRawFilePath file
++ " not to be present in "
++ fromRepoName repo ++ ", but it is present."
-applySimCommand' (CommandNumCopies n) st = Right $ Right $ st
- { simNumCopies = configuredNumCopies n
- }
-applySimCommand' (CommandMinCopies n) st = Right $ Right $ st
- { simMinCopies = configuredMinCopies n
- }
-applySimCommand' (CommandTrustLevel repo trustlevel) st =
+applySimCommand' (CommandNumCopies n) st _ =
+ Right $ Right $ st
+ { simNumCopies = configuredNumCopies n
+ }
+applySimCommand' (CommandMinCopies n) st _ =
+ Right $ Right $ st
+ { simMinCopies = configuredMinCopies n
+ }
+applySimCommand' (CommandTrustLevel repo trustlevel) st _ =
checkKnownRepo repo st $ \u ->
Right $ Right $ st
{ simTrustLevels = M.insert u trustlevel
(simTrustLevels st)
}
-applySimCommand' (CommandGroup repo groupname) st = checkKnownRepo repo st $ \u ->
- Right $ Right $ st
- { simGroups = M.insertWith S.union u
- (S.singleton groupname)
- (simGroups st)
- }
-applySimCommand' (CommandUngroup repo groupname) st = checkKnownRepo repo st $ \u ->
- Right $ Right $ st
- { simGroups = M.adjust (S.delete groupname) u (simGroups st)
- }
-applySimCommand' (CommandWanted repo expr) st = checkKnownRepo repo st $ \u ->
- checkValidPreferredContentExpression expr $ Right $ st
- { simWanted = M.insert u expr (simWanted st)
- }
-applySimCommand' (CommandRequired repo expr) st = checkKnownRepo repo st $ \u ->
- checkValidPreferredContentExpression expr $ Right $ st
- { simRequired = M.insert u expr (simRequired st)
- }
-applySimCommand' (CommandGroupWanted groupname expr) st =
+applySimCommand' (CommandGroup repo groupname) st _ =
+ checkKnownRepo repo st $ \u ->
+ Right $ Right $ st
+ { simGroups = M.insertWith S.union u
+ (S.singleton groupname)
+ (simGroups st)
+ }
+applySimCommand' (CommandUngroup repo groupname) st _ =
+ checkKnownRepo repo st $ \u ->
+ Right $ Right $ st
+ { simGroups = M.adjust (S.delete groupname) u (simGroups st)
+ }
+applySimCommand' (CommandWanted repo expr) st _ =
+ checkKnownRepo repo st $ \u ->
+ checkValidPreferredContentExpression expr $ Right $ st
+ { simWanted = M.insert u expr (simWanted st)
+ }
+applySimCommand' (CommandRequired repo expr) st _ =
+ checkKnownRepo repo st $ \u ->
+ checkValidPreferredContentExpression expr $ Right $ st
+ { simRequired = M.insert u expr (simRequired st)
+ }
+applySimCommand' (CommandGroupWanted groupname expr) st _ =
checkValidPreferredContentExpression expr $ Right $ st
{ simGroupWanted = M.insert groupname expr (simGroupWanted st)
}
-applySimCommand' (CommandMaxSize repo sz) st = checkKnownRepo repo st $ \u ->
+applySimCommand' (CommandMaxSize repo sz) st _ =
+ checkKnownRepo repo st $ \u ->
+ Right $ Right $ st
+ { simMaxSize = M.insert u sz (simMaxSize st)
+ }
+applySimCommand' (CommandRebalance b) st _ =
Right $ Right $ st
- { simMaxSize = M.insert u sz (simMaxSize st)
+ { simRebalance = b
}
-applySimCommand' (CommandRebalance b) st = Right $ Right $ st
- { simRebalance = b
- }
-applySimCommand' (CommandComment _) st = Right $ Right st
-applySimCommand' CommandBlank st = Right $ Right st
+applySimCommand' (CommandComment _) st _ = Right $ Right st
+applySimCommand' CommandBlank st _ = Right $ Right st
applySimAction
:: RepoName
-> UUID
-> SimAction
- -> SimState
- -> Either String (Either (Annex SimState) SimState)
+ -> SimState SimRepo
+ -> Either String (Either (Annex (SimState SimRepo)) (SimState SimRepo))
applySimAction _r _u (ActionPull _remote) _st = undefined -- TODO
applySimAction _r _u (ActionPush _remote) _st = undefined -- TODO
applySimAction r u (ActionGetWanted remote) st =
-> RemoteName
-> (UUID -> S.Set UUID -> Bool)
-> (Maybe Key -> AssociatedFile -> UUID -> Annex Bool)
- -> (UUID -> RawFilePath -> Key -> RepoName -> SimState -> SimState)
- -> SimState
- -> Either String (Either (Annex SimState) SimState)
+ -> (UUID -> RawFilePath -> Key -> RepoName -> SimState SimRepo -> SimState SimRepo)
+ -> SimState SimRepo
+ -> Either String (Either (Annex (SimState SimRepo)) (SimState SimRepo))
overFilesRemote r u remote remotepred checkwant handlewanted st =
checkKnownRemote remote r u st $ \remoteu ->
Right $ Left $ liftIO $
- runSimRepo u st $ \rst ->
+ runSimRepo u st $ \rst -> do
let l = M.toList $
M.filter (checkremotepred remoteu rst) $
simFiles st
- in go remoteu l st
+ go remoteu l st
where
go _ [] st' = return st'
go remoteu ((f, k):rest) st' = do
simulateGitAnnexMerge
:: RepoName
-> RepoName
- -> SimState
- -> Either String (Either (Annex SimState) SimState)
+ -> SimState SimRepo
+ -> Either String (Either (Annex (SimState SimRepo)) (SimState SimRepo))
simulateGitAnnexMerge src dest st =
case (M.lookup src (simRepos st), M.lookup dest (simRepos st)) of
(Just srcu, Just destu) -> case M.lookup destu (simRepoState st) of
}
_ -> Left $ "Unable to find " ++ fromRepoName src ++ " or " ++ fromRepoName dest ++ " in simRepos"
-checkNonexistantRepo :: RepoName -> SimState -> Either String a -> Either String a
+checkNonexistantRepo :: RepoName -> SimState SimRepo -> Either String a -> Either String a
checkNonexistantRepo reponame st a = case M.lookup reponame (simRepos st) of
Nothing -> a
Just _ -> Left $ "There is already a repository in the simulation named \""
++ fromRepoName reponame ++ "\"."
-checkKnownRepo :: RepoName -> SimState -> (UUID -> Either String a) -> Either String a
+checkKnownRepo :: RepoName -> SimState SimRepo -> (UUID -> Either String a) -> Either String a
checkKnownRepo reponame st a = case M.lookup reponame (simRepos st) of
Just u -> a u
Nothing -> Left $ "No repository in the simulation is named \""
++ fromRepoName reponame ++ "\"."
-checkKnownRemote :: RemoteName -> RepoName -> UUID -> SimState -> (UUID -> Either String a) -> Either String a
+checkKnownRemote :: RemoteName -> RepoName -> UUID -> SimState SimRepo -> (UUID -> Either String a) -> Either String a
checkKnownRemote remotename reponame u st a =
let rs = fromMaybe mempty $ M.lookup u (simConnections st)
in if S.member remotename rs
Nothing -> Right v
Just e -> Left $ "Failed parsing \"" ++ expr ++ "\": " ++ e
-simRandom :: SimState -> (StdGen -> (v, StdGen)) -> (v -> r) -> (r, SimState)
+simRandom :: SimState t -> (StdGen -> (v, StdGen)) -> (v -> r) -> (r, SimState t)
simRandom st mk f =
- let (v, rng) = mk (simRng st)
- in (f v, st { simRng = rng })
+ let rng = mkStdGen (simRng st)
+ (v, rng') = mk rng
+ (newseed, _) = random rng'
+ in (f v, st { simRng = newseed })
-randomRepo :: SimState -> (Maybe (RepoName, UUID), SimState)
+randomRepo :: SimState SimRepo -> (Maybe (RepoName, UUID), SimState SimRepo)
randomRepo st
- | null (simRepoList st) = (Nothing, st)
+ | null repolist = (Nothing, st)
| otherwise = simRandom st
- (randomR (0, length (simRepoList st) - 1)) $ \n -> do
- let r = simRepoList st !! n
+ (randomR (0, length repolist - 1)) $ \n -> do
+ let r = repolist !! n
u <- M.lookup r (simRepos st)
return (r, u)
+ where
+ repolist = M.keys (simRepos st)
-randomAction :: UUID -> SimState -> (SimAction, SimState)
+randomAction :: UUID -> SimState SimRepo -> (SimAction, SimState SimRepo)
randomAction u st = case M.lookup u (simConnections st) of
Just cs | not (S.null cs) ->
let (mkact, st') = simRandom st (randomR (0, length mkactions - 1))
let (w, g') = random g
in go (w:c) (pred n) g'
-genSimKey :: ByteSize -> SimState -> (Key, SimState)
+genSimKey :: ByteSize -> SimState t -> (Key, SimState t)
genSimKey sz st = simRandom st (randomWords 1024) mk
where
mk b =
let tk = genTestKey $ L.pack b
in alterKey tk $ \kd -> kd { keySize = Just sz }
-genSimUUID :: SimState -> RepoName -> (UUID, SimState)
+genSimUUID :: SimState t -> RepoName -> (UUID, SimState t)
genSimUUID st (RepoName reponame) = simRandom st (randomWords 1024)
(\l -> genUUIDInNameSpace simUUIDNameSpace (encodeBS reponame <> B.pack l))
instance Show GetExistingRepoByName where
show _ = "GetExistingRepoByName"
-newtype GetSimRepoPath = GetSimRepoPath (UUID -> FilePath)
-
-instance Show GetSimRepoPath where
- show _ = "GetSimRepoPath"
-
data SimRepoConfig = SimRepoConfig
{ simRepoConfigUUID :: UUID
, simRepoConfigIsSpecialRemote :: Bool
, simRepoConfigMaxSize = Nothing
}
-addRepo :: RepoName -> SimRepoConfig -> SimState -> SimState
+addRepo :: RepoName -> SimRepoConfig -> SimState SimRepo -> SimState SimRepo
addRepo reponame simrepo st = st
{ simRepos = M.insert reponame u (simRepos st)
- , simRepoList = if reponame `elem` simRepoList st
- then simRepoList st
- else reponame : simRepoList st
, simRepoState = M.insert u rst (simRepoState st)
, simConnections = M.insert u mempty (simConnections st)
, simGroups = M.insert u (simRepoConfigGroups simrepo) (simGroups st)
data SimRepo = SimRepo
{ simRepoGitRepo :: Repo
, simRepoAnnex :: (Annex.AnnexState, Annex.AnnexRead)
- , simRepoCurrState :: SimState
+ , simRepoCurrState :: SimState SimRepo
, simRepoUUID :: UUID
}
show _ = "SimRepo"
{- Inits and updates SimRepos to reflect the SimState. -}
-updateSimRepos :: SimState -> IO SimState
+updateSimRepos :: SimState SimRepo -> IO (SimState SimRepo)
updateSimRepos st = updateSimRepoStates st >>= initNewSimRepos
-updateSimRepoStates :: SimState -> IO SimState
+updateSimRepoStates :: SimState SimRepo -> IO (SimState SimRepo)
updateSimRepoStates st = go st (M.toList $ simRepoState st)
where
go st' [] = return st'
go st'' rest
Nothing -> go st' rest
-initNewSimRepos :: SimState -> IO SimState
+initNewSimRepos :: SimState SimRepo -> IO (SimState SimRepo)
initNewSimRepos = \st -> go st (M.toList $ simRepoState st)
where
go st [] = return st
go st ((u, rst):rest) =
case simRepo rst of
Nothing -> do
- let GetSimRepoPath getdest = simGetSimRepoPath st
- sr <- initSimRepo (simRepoName rst) u (getdest u) st
+ let d = simRepoDirectory st u
+ sr <- initSimRepo (simRepoName rst) u d st
let rst' = rst { simRepo = Just sr }
let st' = st
{ simRepoState = M.insert u rst'
go st' rest
_ -> go st rest
-initSimRepo :: RepoName -> UUID -> FilePath -> SimState -> IO SimRepo
+simRepoDirectory :: SimState t -> UUID -> FilePath
+simRepoDirectory st u = simRootDirectory st </> fromUUID u
+
+initSimRepo :: RepoName -> UUID -> FilePath -> SimState SimRepo -> IO SimRepo
initSimRepo simreponame u dest st = do
inited <- boolSystem "git"
[ Param "init"
updateSimRepoState st $ SimRepo
{ simRepoGitRepo = simrepo
, simRepoAnnex = ast'
- , simRepoCurrState = emptySimState
- (simRng st)
- (simGetExistingRepoByName st)
- (simGetSimRepoPath st)
+ , simRepoCurrState =
+ emptySimState (simRng st) (simRootDirectory st)
, simRepoUUID = u
}
simulationDifferences :: Differences
simulationDifferences = mkDifferences $ S.singleton Simulation
-runSimRepo :: UUID -> SimState -> (SimRepoState -> Annex SimState) -> IO SimState
+runSimRepo :: UUID -> SimState SimRepo -> (SimRepoState SimRepo -> Annex (SimState SimRepo)) -> IO (SimState SimRepo)
runSimRepo u st a = do
st' <- updateSimRepos st
case M.lookup u (simRepoState st') of
Nothing -> error $ "runSimRepo simRepo not set for " ++ fromUUID u
Nothing -> error $ "runSimRepo simRepoState not found for " ++ fromUUID u
-updateSimRepoState :: SimState -> SimRepo -> IO SimRepo
+updateSimRepoState :: SimState SimRepo -> SimRepo -> IO SimRepo
updateSimRepoState newst sr = do
((), (ast, ard)) <- Annex.run (simRepoAnnex sr) $ doQuietAction $ do
let oldst = simRepoCurrState sr
-> DiffUpdate a b m
-> m ()
updateField old new f = updateMap (f old) (f new)
+
+suspendSim :: SimState SimRepo -> IO ()
+suspendSim st = do
+ -- Update the sim repos before suspending, so that at restore time
+ -- they are up-to-date.
+ st' <- updateSimRepos st
+ let st'' = st'
+ { simRepoState = M.map freeze (simRepoState st)
+ }
+ writeFile (simRootDirectory st </> "state") (show st'')
+ where
+ freeze :: SimRepoState SimRepo -> SimRepoState ()
+ freeze rst = rst { simRepo = Nothing }
+
+restoreSim :: RawFilePath -> IO (Either String (SimState SimRepo))
+restoreSim rootdir =
+ tryIO (readFile (fromRawFilePath rootdir </> "state")) >>= \case
+ Left err -> return (Left (show err))
+ Right c -> case readMaybe c :: Maybe (SimState ()) of
+ Nothing -> return (Left "unable to parse sim state file")
+ Just st -> do
+ repostate <- M.fromList
+ <$> mapM (thaw st) (M.toList (simRepoState st))
+ let st' = st
+ { simRepoState =
+ M.map (finishthaw st') repostate
+ }
+ return (Right st')
+ where
+ thaw st (u, rst) = tryNonAsync (thaw' st u) >>= return . \case
+ Left _ -> (u, rst { simRepo = Nothing })
+ Right r -> (u, rst { simRepo = Just r })
+ thaw' st u = do
+ simrepo <- Git.Construct.fromPath $ toRawFilePath $
+ simRepoDirectory st u
+ ast <- Annex.new simrepo
+ return $ SimRepo
+ { simRepoGitRepo = simrepo
+ , simRepoAnnex = ast
+ , simRepoCurrState =
+ -- Placeholder, replaced later with current
+ -- state.
+ emptySimState (simRng st)
+ (simRootDirectory st)
+ , simRepoUUID = u
+ }
+ finishthaw st rst = rst
+ { simRepo = case simRepo rst of
+ Nothing -> Nothing
+ Just sr -> Just $ sr { simRepoCurrState = st }
+ }