{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
module Data.Git.EditTree where
import Prelude hiding (fail)
import Control.Monad.Fail
import Control.Monad.State hiding (fail)
import qualified Data.ByteString as B
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as BL
import Data.Git
import Data.Map (Map)
import qualified Data.Map as Map
import System.Posix.FilePath
type EditTree = Map TreeEntry TreePart
data TreePart = PartSha Sha1 | PartData ByteString | PartTree EditTree
newtype TreeEdit m a = TreeEdit { runTreeEdit :: StateT EditTree m a }
deriving (Functor, Applicative, Monad, MonadState EditTree, MonadIO, MonadTrans, MonadFail)
loadEditTree :: (MonadGit m, MonadFail m) => Sha1 -> m EditTree
loadEditTree r = do (Just (Tree ents)) <- findTreeish r
return $ fmap PartSha ents
writeEditTree :: MonadGit m => EditTree -> m Sha1
writeEditTree = writeTree . Tree <=< traverse partToSha
where partToSha (PartSha s) = return s
partToSha (PartData b) = writeBlob $ Blob b
partToSha (PartTree t) = writeEditTree t
don'tEditTree :: MonadGit m => EditTree -> TreeEdit m a -> m EditTree
don'tEditTree et te = execStateT (runTreeEdit te) et
editTree :: MonadGit m => EditTree -> TreeEdit m a -> m EditTree
editTree et te = do et' <- don'tEditTree et te
_ <- writeEditTree et'
return et'
rm :: Monad m => TreeEntry -> TreeEdit m ()
rm = modify . Map.delete
cd :: (MonadFail m, MonadGit m) => RawFilePath -> TreeEdit m a -> TreeEdit m a
cd dir act = splitPathComponents dir >>= (`cd'` act)
cd' :: (MonadFail m, MonadGit m) => [PathComponent] -> TreeEdit m a -> TreeEdit m a
cd' [] te = te
cd' (d:ds) te = cd1 d (cd' ds te)
cd1 :: (MonadFail m, MonadGit m) => PathComponent -> TreeEdit m a -> TreeEdit m a
cd1 d te = do old <- get
let dir = Entry d TreeMode
case old Map.! dir of
PartTree et -> put et
PartSha r -> put =<< lift (loadEditTree r)
_ -> error "cd1 exploded"
ret <- te
modify (\et -> Map.insert dir (PartTree et) old)
return ret
create :: Monad m => TreeEntry -> TreePart -> TreeEdit m ()
create name ent = modify (Map.insert name ent)
mkdir :: MonadFail m => PathComponent -> TreeEdit m ()
mkdir dir = modify (Map.insertWith (flip const) (Entry dir TreeMode) (PartTree mempty))
cdCreating :: (MonadFail m, MonadGit m) => RawFilePath -> TreeEdit m a -> TreeEdit m a
cdCreating path te = (`cdCreating'` te) =<< splitPathComponents path
cdCreating' :: (MonadFail m, MonadGit m) => [PathComponent] -> TreeEdit m a -> TreeEdit m a
cdCreating' path te = go path
where go [] = te
go (d:ds) = mkdir d >> cd1 d (go ds)
createFiles :: Monad m => Map PathComponent B.ByteString -> TreeEdit m ()
createFiles m = sequence_ [create (Entry file BlobMode) $ PartData (BL.fromStrict blob)
| (file, blob) <- Map.toList m]