module Filediff
(
diffFiles
, diffDirectories
, applyToFile
, applyToDirectory
) where
import qualified System.IO as IO
import qualified System.Directory as D
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Maybe (isJust, fromJust, catMaybes)
import Data.List ((\\), intersect)
import Data.Monoid
import Control.Applicative
import Control.Monad
import Control.Monad.Trans.Either
import Control.Monad.IO.Class (liftIO)
import Filediff.Types
import Filediff.Sequence (SeqDiff(..), diffSequences, applySequenceDiff)
import Filediff.Utils
( (</>)
, (<.>)
, getFileDirectory
, removeDotDirs
, createFileWithContents
, dropUntil
, removeFirstPathComponent
, getDirectoryContentsRecursiveSafe )
diffFiles :: FilePath -> FilePath -> IO Filediff
diffFiles a b = do
aIsDir <- D.doesDirectoryExist a
bIsDir <- D.doesDirectoryExist b
when (aIsDir || bIsDir) $ error $ "One or both of " ++ a ++ " and " ++ b ++ "is not a file, but a directory."
aExists <- D.doesFileExist a
bExists <- D.doesFileExist b
aLines <- if aExists then T.lines <$> TIO.readFile a else return []
bLines <- if bExists then T.lines <$> TIO.readFile b else return []
let linediff = diffSequences aLines bLines
return Filediff
{ base = a
, comp = b
, linediff = linediff }
diffDirectories :: FilePath -> FilePath -> IO Diff
diffDirectories a b = do
aIsFile <- D.doesFileExist a
bIsFile <- D.doesFileExist b
when (aIsFile || bIsFile) $ error $ "One or both of " ++ a ++ " and " ++ b ++ "is not a directory, but a file."
aContents <- getDirectoryContentsRecursiveSafe a
bContents <- getDirectoryContentsRecursiveSafe b
intersectionDiffs <- getDiffs $ intersect aContents bContents
aOnlyDiffs <- getDiffs $ aContents \\ bContents
bOnlyDiffs <- getDiffs $ bContents \\ aContents
let allDiffs = map removeFirstPathComponentFromDiff $ intersectionDiffs ++ aOnlyDiffs ++ bOnlyDiffs
return $ Diff allDiffs
where
getDiffs :: [FilePath] -> IO [Filediff]
getDiffs filepaths
= filter (not . isIdentityFileDiff)
<$> mapM (\fp -> diffFiles (a </> fp) (b </> fp)) filepaths
isIdentityFileDiff :: Filediff -> Bool
isIdentityFileDiff = (==) mempty . linediff
removeFirstPathComponentFromDiff :: Filediff -> Filediff
removeFirstPathComponentFromDiff (Filediff base comp seqdiff)
= Filediff
(removeFirstPathComponent base)
(removeFirstPathComponent comp)
seqdiff
applyToFile :: Filediff -> FilePath -> IO [Line]
applyToFile (Filediff _ _ linediff) filepath = do
exists <- D.doesFileExist filepath
when (not exists) $ createFileWithContents filepath ""
fileContents <- TIO.readFile filepath
let result = (applySequenceDiff linediff . T.lines) fileContents
TIO.writeFile filepath (T.unlines result)
return result
applyToDirectory :: Diff -> FilePath -> IO ()
applyToDirectory (Diff filediffs) filepath = mapM_ apply filediffs
where
apply :: Filediff -> IO [Line]
apply diff@(Filediff base compare linediff)
= applyToFile diff (filepath </> base)