{-# LANGUAGE GADTs, TypeSynonymInstances, FlexibleInstances, ScopedTypeVariables #-} module Shuffle where import Control.Monad.State ( gets, modify ) import qualified Data.Map as M import qualified Data.Set as S import System.FSLib.Hash ( Hash(..) ) import System.FSLib.StoreMonad ( LoadMonad(..), StoreMonad(..), Load(..), Store(..) ) import Data.Patch.Commute ( commute, CommuteMonad(..), commuteFL ) import Data.Patch.Named ( infopatch, patchcontents, Named ) import Data.Patch.Info ( hashPI, info ) import Data.Patch.Prim.V3.Core ( Prim ) import Data.Patch.Prim.V3 () import Witnesses.Ordered ( (:>)(..), FL(..) ) import Witnesses.Unsafe ( unsafeCoerceP ) import Debug.Trace import Types import Repository instance CommuteMonad Gorsvet where commuteFail = fail "Error commuting patches." loadPatch :: (LoadMonad m) => Repository m -> Hash -> m (Named Prim x y) loadPatch r ih = do inf <- load ih patch <- load $ case M.lookup ih $ rInventory r of Nothing -> error $ "loadPatch " ++ show ih Just x -> x return $ infopatch inf patch loadFL :: (LoadMonad m) => Repository m -> [Hash] -> m (FL (Named Prim) x y) loadFL r [] = return $ unsafeCoerceP NilFL loadFL r (x:xs) = do x' <- loadPatch r x xs' <- loadFL r xs return $ x' :>: xs' storePatch :: (StoreMonad m) => Repository m -> Named Prim x y -> m (Repository m) storePatch r p = do phash <- store (patchcontents p) return $ r { rInventory = M.insert (hashPI $ info p) phash $ rInventory r } storeFL :: (StoreMonad m) => Repository m -> (FL (Named Prim) x y) -> m (Repository m) storeFL r NilFL = return r storeFL r (p:>:ps) = do r' <- storePatch r p storeFL r' ps shuffleRepo :: forall m. (CommuteMonad m, StoreMonad m) => Repository m -> S.Set Hash -> m (Repository m) shuffleRepo orig alltodo = shuffle' orig alltodo S.empty where shuffle' :: Repository m -> S.Set Hash -> S.Set Hash -> m (Repository m) shuffle' repo todoset _ | S.null todoset = return repo shuffle' repo todoset doneset = do let (done, todo) = span (`S.member` doneset) $ rOrder repo (topush, topull:rest) = span (`S.notMember` todoset) $ trace (show todo) todo topushFL <- loadFL repo $ reverse topush topullP <- loadPatch repo topull (pushed :> pulled) <- commuteFL (topullP :> topushFL) repo' <- (\r -> storeFL r pushed) =<< storePatch repo pulled let repo'' = repo' { rOrder = done ++ (topull:topush) ++ rest } shuffle' repo'' (S.delete topull todoset) (S.insert topull doneset) shuffle :: S.Set Hash -> Gorsvet () shuffle phashes = do repo <- gets gsRepository repo' <- shuffleRepo repo phashes modify $ \x -> x { gsRepository = repo' } return () -- | Take two repositories and shuffle them so that all common patches are -- moved to a common prefix. The local repository is shuffled in place, while -- the shuffled remote repository is returned. coShuffle :: Repository Gorsvet -> Gorsvet (Repository Gorsvet) coShuffle remote = do ours <- inventory let theirs = rInventory remote extra = M.difference ours theirs -- stuff we have extra missing = M.difference theirs ours -- stuff we don't have shuffle $ M.keysSet extra -- move extras to top remote' <- shuffleRepo remote $ M.keysSet missing return remote'