module Filediff.Utils
(
(</>)
, (<.>)
, getFileDirectory
, removeDotDirs
, createFileWithContents
, removeFirstPathComponent
, removePathComponents
, getDirectoryContentsRecursiveSafe
, dropInitialSlash
, dropTrailingSlash
, dropUntil
, isPrefix
, dropPrefix
) where
import Data.List ((\\), inits)
import Control.Monad
import Control.Applicative
import qualified System.IO as IO
import qualified System.Directory as D
(</>) :: FilePath -> FilePath -> FilePath
a </> b = a ++ "/" ++ b
(<.>) :: (Functor f) => (b -> c) -> (a -> f b) -> (a -> f c)
f <.> g = \a -> f <$> (g a)
(?:) :: (a -> Bool) -> a -> a -> a
(?:) f a' a = if f a then a else a'
getFileDirectory :: FilePath -> FilePath
getFileDirectory filepath
= (?:) ((/=) "") "."
. reverse
. dropWhile ((/=) '/')
. reverse
$ filepath
removeDotDirs :: [FilePath] -> [FilePath]
removeDotDirs = flip (\\) $ [".", ".."]
createFileWithContents :: FilePath -> String -> IO ()
createFileWithContents filepath contents = do
let intermediateDirs = filter ((==) '/' . last) . tail . inits $ filepath
dirsToCreate <- filterM (not <.> D.doesDirectoryExist) intermediateDirs
mapM_ D.createDirectory dirsToCreate
handle <- IO.openFile filepath IO.WriteMode
IO.hPutStr handle contents
IO.hClose handle
removeFirstPathComponent :: FilePath -> FilePath
removeFirstPathComponent path =
if null . filter ((==) '/') $ path
then error "path without '/' in it"
else tail . dropUntil ((==) '/') $ path
removePathComponents :: Int -> FilePath -> FilePath
removePathComponents k
= last
. take k
. iterate removeFirstPathComponent
getDirectoryContentsRecursiveSafe :: FilePath -> IO [FilePath]
getDirectoryContentsRecursiveSafe directory = do
contents <- getDirectoryContentsRecursiveSafe' directory
let directoryWithTrailingSlash = if last directory == '/'
then directory
else directory </> ""
let numPathComponents = length . filter ((==) '/') $ directoryWithTrailingSlash
return . map (removePathComponents $ numPathComponents + 1) $ contents
getDirectoryContentsRecursiveSafe' :: FilePath -> IO [FilePath]
getDirectoryContentsRecursiveSafe' directory = do
exists <- D.doesDirectoryExist directory
if not exists
then return []
else do
relativeContents <- removeDotDirs <$> D.getDirectoryContents directory
let contents = map ((</>) directory) relativeContents
files <- filterM D.doesFileExist contents
directories <- filterM D.doesDirectoryExist contents
recFiles <- concat <$> mapM getDirectoryContentsRecursiveSafe' directories
return $ files ++ recFiles
dropInitialSlash :: String -> String
dropInitialSlash ('/':s) = s
dropInitialSlash s = s
dropTrailingSlash :: String -> String
dropTrailingSlash [] = []
dropTrailingSlash s = if last s == '/'
then init s
else s
dropUntil :: (a -> Bool) -> [a] -> [a]
dropUntil _ [] = []
dropUntil f (x:xs) =
if f x
then (x:xs)
else dropUntil f xs
isPrefix :: (Eq a) => [a] -> [a] -> Bool
a `isPrefix` b = (==) (length a) . length . takeWhile id $ zipWith (==) a b
dropPrefix :: (Eq a) => [a] -> [a] -> [a]
dropPrefix [] bs = bs
dropPrefix (a:as) (b:bs)
| a /= b = error "not a prefix"
| otherwise = dropPrefix as bs