darcs-2.18.1: src/Darcs/Patch/Prim/FileUUID/Apply.hs
{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings, UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Darcs.Patch.Prim.FileUUID.Apply ( hunkEdit, ObjectMap(..) ) where
import Darcs.Prelude
import Control.Monad.Catch ( MonadThrow(throwM) )
import Control.Monad.State( StateT, runStateT, gets, lift, put )
import qualified Data.ByteString as B
import qualified Data.Map as M
import Darcs.Patch.Apply ( Apply(..) )
import Darcs.Patch.ApplyMonad
( ApplyMonad(..), ApplyMonadTrans(..)
, ApplyMonadOperations
)
import Darcs.Patch.Prim.Class ( PrimApply(..) )
import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Hunk(..), HunkMove(..) )
import Darcs.Patch.Prim.FileUUID.Show
import Darcs.Patch.Prim.FileUUID.ObjectMap
import Darcs.Patch.Repair ( RepairToFL(..) )
import Darcs.Patch.Witnesses.Ordered ( FL(..) )
import Darcs.Util.Printer( text, packedString, ($$), renderString )
instance Apply Prim where
type ApplyState Prim = ObjectMap
apply (Manifest i (L dirid name)) = editDirectory dirid (addObject name i dirid)
apply (Demanifest i (L dirid name)) = editDirectory dirid (delObject name i dirid)
apply (Hunk i hunk) = editFile i (hunkEdit hunk)
apply (HunkMove (HM fs ls ft lt c)) =
editFile fs (hunkEdit (H ls c B.empty)) >> editFile ft (hunkEdit (H lt B.empty c))
apply Identity = return ()
instance RepairToFL Prim where
applyAndTryToFixFL p = apply p >> return Nothing
instance PrimApply Prim where
applyPrimFL NilFL = return ()
applyPrimFL (p :>: ps) = apply p >> applyPrimFL ps
addObject :: Name -> UUID -> UUID -> DirContent -> Either String DirContent
addObject name obj dirid dir
| Just obj' <- M.lookup name dir =
Left $ unwords
[ "##error applying patch: cannot insert"
, show (name, obj)
, "into directory"
, show dirid
, "because another object"
, show obj'
, "with that name already exists"
]
| otherwise = Right $ M.insert name obj dir
delObject :: Name -> UUID -> UUID -> DirContent -> Either String DirContent
delObject name obj dirid dir =
case M.lookup name dir of
Just obj'
| obj == obj' -> Right $ M.delete name dir
| otherwise ->
Left $ unwords
[ "##error applying patch: cannot remove"
, show (name, obj)
, "from directory"
, show dirid
, "because it contains a different object"
, show obj'
]
Nothing ->
Left $ unwords
[ "##error applying patch: cannot remove"
, show (name, obj)
, "from directory"
, show dirid
, "because it does not contain any object of that name"
]
hunkEdit :: Hunk wX wY -> FileContent -> Either String FileContent
hunkEdit h@(H off old new) c
| old `B.isPrefixOf` (B.drop off c) =
Right $ B.concat [B.take off c, new, B.drop (off + B.length old) c]
| otherwise =
Left $ renderString $
text "##error applying hunk:" $$ displayHunk Nothing h $$ "##to" $$
packedString c
-- $$ text "##old=" <> text (ppShow old) $$
-- text "##new=" <> text (ppShow new) $$
-- text "##c=" <> text (ppShow c)
editObject :: MonadThrow m
=> UUID
-> (Maybe (Object m) -> Either String (Object m))
-> (StateT (ObjectMap m) m) ()
editObject i edit = do
load <- gets getObject
store <- gets putObject
obj <- lift $ load i
obj' <- liftEither $ edit obj
new <- lift $ store i $ obj'
put new
-- a semantic, ObjectMap-based interface for patch application
class ApplyMonadObjectMap m where
editFile :: UUID -> (FileContent -> Either String FileContent) -> m ()
editDirectory :: UUID -> (DirContent -> Either String DirContent) -> m ()
type instance ApplyMonadOperations ObjectMap = ApplyMonadObjectMap
instance MonadThrow m => ApplyMonad ObjectMap (StateT (ObjectMap m) m) where
readFilePS i = do
load <- gets getObject
mobj <- lift $ load i
case mobj of
Just (Blob readBlob _) -> lift readBlob
Just _ -> throwM $ userError $ "readFilePS " ++ show i ++ ": object is not a file"
Nothing -> throwM $ userError $ "readFilePS " ++ show i ++ ": no such file"
liftEither :: MonadThrow m => Either String a -> m a
liftEither (Left e) = throwM $ userError e
liftEither (Right v) = return v
instance MonadThrow m => ApplyMonadObjectMap (StateT (ObjectMap m) m) where
editFile i edit = editObject i edit'
where
edit' (Just (Blob x _)) = Right $ Blob (liftEither . edit =<< x) Nothing
edit' Nothing = Right $ Blob (liftEither $ edit "") Nothing
edit' (Just (Directory _)) =
Left $ "wrong kind of object: " ++ show i ++ " is a directory, not a file"
editDirectory i edit = editObject i edit'
where
edit' (Just (Directory x)) = Directory <$> edit x
edit' Nothing = Directory <$> edit M.empty
edit' (Just (Blob _ _)) =
Left $ "wrong kind of object: " ++ show i ++ " is a file, not a directory"
instance MonadThrow m => ApplyMonadTrans ObjectMap m where
type ApplyMonadOver ObjectMap m = StateT (ObjectMap m) m
runApplyMonad = runStateT