-- Copyright (C) 2009 Benedikt Schmidt -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2, or (at your option) -- any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; see the file COPYING. If not, write to -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. {-# LANGUAGE MultiParamTypeClasses #-} module Darcs.Patch.Index.Monad ( withPatchMods , applyToFileMods , FileMod(..) ) where import Darcs.Prelude import Darcs.Patch.Apply ( Apply(..) ) import Darcs.Patch.ApplyMonad ( ApplyMonad(..), ApplyMonadTree(..) ) import Control.Monad ( when, forM_ ) import Control.Monad.Catch ( MonadThrow(..), SomeException ) import Control.Monad.State ( MonadState, StateT, execStateT, gets, modify ) import Control.Arrow import Darcs.Util.Path ( AnchoredPath, anchorPath, movedirfilename, isPrefix ) import qualified Data.Set as S import Data.Set ( Set ) import Darcs.Util.Tree (Tree) -- | This is used to track changes to files data FileMod a = PTouch a | PCreateFile a | PCreateDir a | PRename a a | PRemove a | PDuplicateTouch a -- ^ this is used for duplicate patches that don't -- have any effect, but we still want to keep -- track of them deriving (Show, Eq, Functor) type FileModState = (Set AnchoredPath, [FileMod AnchoredPath]) newtype FileModMonad a = FMM (StateT FileModState (Either SomeException) a) deriving ( Functor , Applicative , Monad , MonadThrow , MonadState FileModState ) withPatchMods :: FileModMonad a -> Set AnchoredPath -> FileModState withPatchMods (FMM m) fps = second reverse $ case execStateT m (fps,[]) of Left e -> error (show e) Right r -> r -- These instances are defined to be used only with -- apply. instance ApplyMonad Tree FileModMonad where readFilePS = error "readFilePS FileModMonad" instance ApplyMonadTree FileModMonad where mDoesDirectoryExist d = do fps <- gets fst return $ S.member d fps mDoesFileExist f = do fps <- gets fst return $ S.member f fps mReadFilePS _ = error "mReadFilePS FileModMonad" mCreateFile = createFile mCreateDirectory = createDir mRemoveFile = remove mRemoveDirectory = remove mRename a b = do fns <- gets fst -- we have to account for directory moves addMod (PRename a b) modifyFps (S.delete a) addFile b forM_ (S.toList fns) $ \fn -> when (a `isPrefix` fn && a /= fn) $ do modifyFps (S.delete fn) let newfn = movedirfilename a b fn addFile newfn addMod (PRename fn newfn) mModifyFilePS f _ = addMod (PTouch f) -- --------------------------------------------------------------------- -- State Handling Functions addMod :: FileMod AnchoredPath -> FileModMonad () addMod pm = modify $ second (pm :) addFile :: AnchoredPath -> FileModMonad () addFile f = modifyFps (S.insert f) createFile :: AnchoredPath -> FileModMonad () createFile fn = do errorIfPresent fn True addMod (PCreateFile fn) addFile fn createDir :: AnchoredPath -> FileModMonad () createDir fn = do errorIfPresent fn False addMod (PCreateDir fn) addFile fn errorIfPresent :: AnchoredPath -> Bool -> FileModMonad () errorIfPresent fn isFile = do fs <- gets fst when (S.member fn fs) $ throwM $ userError $ unwords [ "error: patch index entry for" , if isFile then "file" else "directory" , anchorPath "" fn , "created >1 times. Run `darcs repair` and try again." ] remove :: AnchoredPath -> FileModMonad () remove f = addMod (PRemove f) >> modifyFps (S.delete f) modifyFps :: (Set AnchoredPath -> Set AnchoredPath) -> FileModMonad () modifyFps f = modify $ first f -------------------------------------------------------------------------------- -- | Apply a patch to set of 'AnchoredPath's, yielding the new set of -- 'AnchoredPath's and 'FileMod's applyToFileMods :: (Apply p, ApplyState p ~ Tree) => p wX wY -> Set AnchoredPath -> FileModState applyToFileMods patch = withPatchMods (apply patch)