packages feed

ipatch-0.1: src/IPatch/Common.hs

{-
 - Copyright (C) 2010 Joachim Breitner
 - 
 - 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 Rank2Types #-}
module IPatch.Common where

import Control.Applicative ( (<$>) )
import Control.Monad (when)
import System.Posix.Files ( getSymbolicLinkStatus, isRegularFile, isDirectory )
import System.Directory ( createDirectoryIfMissing, doesFileExist, removeFile )
import System.FilePath.Posix ( (</>), takeDirectory, normalise )

import Darcs.Arguments ( DarcsFlag(LookForAdds) )
import Darcs.Repository
    ( createRepository,
      applyToWorking,
      finalizeRepositoryChanges,
      tentativelyAddPatch,
      withGutsOf,
      withRepoLock,
      invalidateIndex,
      unrecordedChanges )
import Darcs.Flags ( Compression(..) )
import Darcs.RepoPath ( AbsolutePath, FilePathLike(..) )
import Darcs.External ( cloneFile )
import Darcs.Lock ( withTempDir )
import Darcs.Patch ( invert, fromPrims, namepatch )
import Darcs.Global ( debugMessage )
import Darcs.Hopefully ( n2pia )
import Darcs.Utils ( clarifyErrors )

import IPatch.DiffFile ( applyDiff )

clonePathWithDeletion :: FilePath -> FilePath -> FilePath -> IO ()
clonePathWithDeletion source dest path = do
    let source' = source </> path
        dest' = dest </> path
    ex <- doesFileExist source'
    if ex
     then do
        fs <- getSymbolicLinkStatus source'
        if isDirectory fs
         then do
            createDirectoryIfMissing True dest'
         else
            if isRegularFile fs
             then do
                createDirectoryIfMissing True (dest </> takeDirectory path)
                cloneFile source' dest'
             else
                fail ("clonePathWithDeletion: Bad file " ++ source')
     else do
        exT <- doesFileExist dest'
        when exT $ removeFile dest'
   `catch` fail ("clonePathWithDeletion: Bad file " ++ source </> path)

clonePathsWithDeletion source dest = mapM_ (clonePathWithDeletion source dest)


withTempRepository :: String -> (AbsolutePath -> IO a) -> IO a
withTempRepository name job =
    withTempDir ("ipatch-repo-" ++ name) $ \rdir -> do
        debugMessage $ "Creating temporary repository in directory " ++ show rdir
        createRepository []
        job rdir

initializeBaseState rdir sdir files = do
    debugMessage $ "Copying " ++ show (length files) ++ " files to temporary repository."  
    clonePathsWithDeletion sdir (toFilePath rdir) files
    -- Create a patch from the newly added files
    debugMessage $ "Creating initial check  in patch"
    withRepoLock [LookForAdds] $ \repo -> do
        init_ps <- unrecordedChanges [LookForAdds] repo [] -- Correct flags?
        init_patch <- n2pia <$> namepatch "NODATE" "Initial state" "NOAUTHOR" [] (fromPrims init_ps)
        tentativelyAddPatch repo [] init_patch
        invalidateIndex repo
        withGutsOf repo (finalizeRepositoryChanges repo)
            `clarifyErrors` "Failed to apply inital patch"
        return init_ps


diffToPrims diff = do
    debugMessage $ "Applying the user provided diff"
    -- Now apply the patch
    applyDiff diff

    debugMessage $ "Creating a patch from the user changes"
    withRepoLock [LookForAdds] $ \repo -> do
        -- Create another patch from the changed files
        patch_ps <- unrecordedChanges [LookForAdds] repo []
        -- patch_patch <- n2pia <$> namepatch date "Patch effect" author [] (fromPrims patch_ps)
        -- tentativelyAddPatch repo [] patch_patch
        -- Now we obliterate the patch, undoing its effects
        applyToWorking repo [] (invert patch_ps) `catch` \e ->
            fail ("Couldn't undo diff effect in working dir.\n" ++ show e)
        return patch_ps
      
stdindefault :: a -> [String] -> IO [String]
stdindefault _ [] = return ["-"]
stdindefault _ x = return x