{-# LANGUAGE CPP #-}
module Data.FileStore.Darcs ( darcsFileStore ) where
import Control.Exception (throwIO)
import Control.Monad (when)
import Data.Time (formatTime)
import Data.FileStore.Compat.Locale (defaultTimeLocale)
import Data.List (sort, isPrefixOf)
#ifdef USE_MAXCOUNT
import Data.List (isInfixOf)
#endif
import System.Exit (ExitCode(..))
import System.Directory (doesDirectoryExist, createDirectoryIfMissing)
import System.FilePath ((</>), dropFileName, addTrailingPathSeparator)
import Data.FileStore.DarcsXml (parseDarcsXML)
import Data.FileStore.Types
import Data.FileStore.Utils (withSanityCheck, hashsMatch, runShellCommand, ensureFileExists, grepSearchRepo, withVerifyDir, encodeArg)
import Data.ByteString.Lazy.UTF8 (toString)
import qualified Data.ByteString.Lazy as B (ByteString, writeFile, null)
darcsFileStore :: FilePath -> FileStore
darcsFileStore :: FilePath -> FileStore
darcsFileStore FilePath
repo = FileStore {
initialize :: IO ()
initialize = FilePath -> IO ()
darcsInit FilePath
repo
, save :: forall a.
Contents a =>
FilePath -> Author -> FilePath -> a -> IO ()
save = FilePath -> FilePath -> Author -> FilePath -> a -> IO ()
forall a.
Contents a =>
FilePath -> FilePath -> Author -> FilePath -> a -> IO ()
darcsSave FilePath
repo
, retrieve :: forall a. Contents a => FilePath -> Maybe FilePath -> IO a
retrieve = FilePath -> FilePath -> Maybe FilePath -> IO a
forall a.
Contents a =>
FilePath -> FilePath -> Maybe FilePath -> IO a
darcsRetrieve FilePath
repo
, delete :: FilePath -> Author -> FilePath -> IO ()
delete = FilePath -> FilePath -> Author -> FilePath -> IO ()
darcsDelete FilePath
repo
, rename :: FilePath -> FilePath -> Author -> FilePath -> IO ()
rename = FilePath -> FilePath -> FilePath -> Author -> FilePath -> IO ()
darcsMove FilePath
repo
, history :: [FilePath] -> TimeRange -> Maybe Int -> IO [Revision]
history = FilePath -> [FilePath] -> TimeRange -> Maybe Int -> IO [Revision]
darcsLog FilePath
repo
, latest :: FilePath -> IO FilePath
latest = FilePath -> FilePath -> IO FilePath
darcsLatestRevId FilePath
repo
, revision :: FilePath -> IO Revision
revision = FilePath -> FilePath -> IO Revision
darcsGetRevision FilePath
repo
, index :: IO [FilePath]
index = FilePath -> IO [FilePath]
darcsIndex FilePath
repo
, directory :: FilePath -> IO [Resource]
directory = FilePath -> FilePath -> IO [Resource]
darcsDirectory FilePath
repo
, search :: SearchQuery -> IO [SearchMatch]
search = FilePath -> SearchQuery -> IO [SearchMatch]
darcsSearch FilePath
repo
, idsMatch :: FilePath -> FilePath -> Bool
idsMatch = (FilePath -> FilePath -> Bool)
-> FilePath -> FilePath -> FilePath -> Bool
forall a b. a -> b -> a
const FilePath -> FilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
hashsMatch FilePath
repo }
runDarcsCommand :: FilePath -> String -> [String] -> IO (ExitCode, String, B.ByteString)
runDarcsCommand :: FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
command [FilePath]
args = do
(status, err, out) <- FilePath
-> Maybe [(FilePath, FilePath)]
-> FilePath
-> [FilePath]
-> IO (ExitCode, ByteString, ByteString)
runShellCommand FilePath
repo Maybe [(FilePath, FilePath)]
forall a. Maybe a
Nothing FilePath
"darcs" (FilePath
command FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: [FilePath]
args)
return (status, toString err, out)
darcsInit :: FilePath -> IO ()
darcsInit :: FilePath -> IO ()
darcsInit FilePath
repo = do
exists <- FilePath -> IO Bool
doesDirectoryExist FilePath
repo
when exists $ withVerifyDir repo $ throwIO RepositoryExists
createDirectoryIfMissing True repo
(status, err, _) <- runDarcsCommand repo "init" []
if status == ExitSuccess
then return ()
else throwIO $ UnknownError $ "darcs init failed:\n" ++ err
darcsSave :: Contents a => FilePath -> FilePath -> Author -> Description -> a -> IO ()
darcsSave :: forall a.
Contents a =>
FilePath -> FilePath -> Author -> FilePath -> a -> IO ()
darcsSave FilePath
repo FilePath
name Author
author FilePath
logMsg a
contents = do
FilePath -> [FilePath] -> FilePath -> IO () -> IO ()
forall b. FilePath -> [FilePath] -> FilePath -> IO b -> IO b
withSanityCheck FilePath
repo [FilePath
"_darcs"] FilePath
name (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePath -> ByteString -> IO ()
B.writeFile (FilePath
repo FilePath -> FilePath -> FilePath
</> FilePath -> FilePath
encodeArg FilePath
name) (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ a -> ByteString
forall a. Contents a => a -> ByteString
toByteString a
contents
FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"add" [FilePath
name]
FilePath -> [FilePath] -> Author -> FilePath -> IO ()
darcsCommit FilePath
repo [FilePath
name] Author
author FilePath
logMsg
darcsCommit :: FilePath -> [FilePath] -> Author -> Description -> IO ()
darcsCommit :: FilePath -> [FilePath] -> Author -> FilePath -> IO ()
darcsCommit FilePath
repo [FilePath]
names Author
author FilePath
logMsg = do
let args :: [FilePath]
args = [FilePath
"--all", FilePath
"-A", (Author -> FilePath
authorName Author
author FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" <" FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ Author -> FilePath
authorEmail Author
author FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
">"), FilePath
"-m", FilePath
logMsg] [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
names
(statusCommit, errCommit, _) <- FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"record" [FilePath]
args
if statusCommit == ExitSuccess
then return ()
else throwIO $ if null errCommit
then Unchanged
else UnknownError $ "Could not darcs record " ++ unwords names ++ "\n" ++ errCommit
darcsMove :: FilePath -> FilePath -> FilePath -> Author -> Description -> IO ()
darcsMove :: FilePath -> FilePath -> FilePath -> Author -> FilePath -> IO ()
darcsMove FilePath
repo FilePath
oldName FilePath
newName Author
author FilePath
logMsg = do
FilePath -> [FilePath] -> FilePath -> IO () -> IO ()
forall b. FilePath -> [FilePath] -> FilePath -> IO b -> IO b
withSanityCheck FilePath
repo [FilePath
"_darcs"] FilePath
newName (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
(statusAdd, _, _) <- FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"add" [FilePath -> FilePath
dropFileName FilePath
newName]
(statusAdd', _,_) <- runDarcsCommand repo "mv" [oldName, newName]
if statusAdd == ExitSuccess && statusAdd' == ExitSuccess
then darcsCommit repo [oldName, newName] author logMsg
else throwIO NotFound
darcsDelete :: FilePath -> FilePath -> Author -> Description -> IO ()
darcsDelete :: FilePath -> FilePath -> Author -> FilePath -> IO ()
darcsDelete FilePath
repo FilePath
name Author
author FilePath
logMsg = FilePath -> [FilePath] -> FilePath -> IO () -> IO ()
forall b. FilePath -> [FilePath] -> FilePath -> IO b -> IO b
withSanityCheck FilePath
repo [FilePath
"_darcs"] FilePath
name (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
FilePath
-> Maybe [(FilePath, FilePath)]
-> FilePath
-> [FilePath]
-> IO (ExitCode, ByteString, ByteString)
runShellCommand FilePath
repo Maybe [(FilePath, FilePath)]
forall a. Maybe a
Nothing FilePath
"rm" [FilePath
name]
FilePath -> [FilePath] -> Author -> FilePath -> IO ()
darcsCommit FilePath
repo [FilePath
name] Author
author FilePath
logMsg
darcsLog :: FilePath -> [FilePath] -> TimeRange -> Maybe Int -> IO [Revision]
darcsLog :: FilePath -> [FilePath] -> TimeRange -> Maybe Int -> IO [Revision]
darcsLog FilePath
repo [FilePath]
names (TimeRange Maybe UTCTime
begin Maybe UTCTime
end) Maybe Int
mblimit = do
(status, err, output) <- FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"changes" ([FilePath] -> IO (ExitCode, FilePath, ByteString))
-> [FilePath] -> IO (ExitCode, FilePath, ByteString)
forall a b. (a -> b) -> a -> b
$ [FilePath
"--xml-output", FilePath
"--summary"] [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
names [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
opts
if status == ExitSuccess
then case parseDarcsXML $ toString output of
Maybe [Revision]
Nothing -> FileStoreError -> IO [Revision]
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO FileStoreError
ResourceExists
Just [Revision]
parsed -> [Revision] -> IO [Revision]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Revision] -> IO [Revision]) -> [Revision] -> IO [Revision]
forall a b. (a -> b) -> a -> b
$
#ifdef USE_MAXCOUNT
[Revision]
parsed
#else
case mblimit of
Just lim -> take lim parsed
Nothing -> parsed
#endif
else throwIO $ UnknownError $ "darcs changes returned error status.\n" ++ err
where
opts :: [FilePath]
opts = Maybe UTCTime -> Maybe UTCTime -> [FilePath]
timeOpts Maybe UTCTime
begin Maybe UTCTime
end [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
limit
limit :: [FilePath]
limit = case Maybe Int
mblimit of
#ifdef USE_MAXCOUNT
Just Int
lim -> [FilePath
"--max-count",Int -> FilePath
forall a. Show a => a -> FilePath
show Int
lim]
#else
Just _ -> []
#endif
Maybe Int
Nothing -> []
timeOpts :: Maybe UTCTime -> Maybe UTCTime ->[String]
timeOpts :: Maybe UTCTime -> Maybe UTCTime -> [FilePath]
timeOpts Maybe UTCTime
b Maybe UTCTime
e = case (Maybe UTCTime
b,Maybe UTCTime
e) of
(Maybe UTCTime
Nothing,Maybe UTCTime
Nothing) -> []
(Just UTCTime
b', Just UTCTime
e') -> UTCTime -> [FilePath]
from UTCTime
b' [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ UTCTime -> [FilePath]
to UTCTime
e'
(Just UTCTime
b', Maybe UTCTime
Nothing) -> UTCTime -> [FilePath]
from UTCTime
b'
(Maybe UTCTime
Nothing, Just UTCTime
e') -> UTCTime -> [FilePath]
to UTCTime
e'
where from :: UTCTime -> [FilePath]
from UTCTime
z = [FilePath
"--match=date \"after " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ UTCTime -> FilePath
undate UTCTime
z FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"\""]
to :: UTCTime -> [FilePath]
to UTCTime
z = [FilePath
"--to-match=date \"before " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ UTCTime -> FilePath
undate UTCTime
z FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"\""]
undate :: UTCTime -> FilePath
undate = UTCTime -> FilePath
toSqlString
toSqlString :: UTCTime -> FilePath
toSqlString = TimeLocale -> FilePath -> UTCTime -> FilePath
forall t. FormatTime t => TimeLocale -> FilePath -> t -> FilePath
formatTime TimeLocale
defaultTimeLocale FilePath
"%FT%X"
darcsGetRevision :: FilePath -> RevisionId -> IO Revision
darcsGetRevision :: FilePath -> FilePath -> IO Revision
darcsGetRevision FilePath
repo FilePath
hash = do (_,_,output) <- FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"changes"
[FilePath
"--xml-output", FilePath
"--summary", FilePath
"--match=hash " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
hash]
let hists = FilePath -> Maybe [Revision]
parseDarcsXML (FilePath -> Maybe [Revision]) -> FilePath -> Maybe [Revision]
forall a b. (a -> b) -> a -> b
$ ByteString -> FilePath
toString ByteString
output
case hists of
Maybe [Revision]
Nothing -> FileStoreError -> IO Revision
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO FileStoreError
NotFound
Just [Revision]
a -> Revision -> IO Revision
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Revision -> IO Revision) -> Revision -> IO Revision
forall a b. (a -> b) -> a -> b
$ [Revision] -> Revision
forall a. HasCallStack => [a] -> a
head [Revision]
a
darcsLatestRevId :: FilePath -> FilePath -> IO RevisionId
darcsLatestRevId :: FilePath -> FilePath -> IO FilePath
darcsLatestRevId FilePath
repo FilePath
name = do
FilePath -> FilePath -> IO ()
ensureFileExists FilePath
repo FilePath
name
#ifdef USE_MAXCOUNT
(status, err, output) <- FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"changes" [FilePath
"--xml-output", FilePath
"--max-count=1", FilePath
name]
when (status /= ExitSuccess && "unrecognized option" `isInfixOf` err) $ throwIO NoMaxCount
#else
(_, _, output) <- runDarcsCommand repo "changes" ["--xml-output", name]
#endif
let patchs = FilePath -> Maybe [Revision]
parseDarcsXML (FilePath -> Maybe [Revision]) -> FilePath -> Maybe [Revision]
forall a b. (a -> b) -> a -> b
$ ByteString -> FilePath
toString ByteString
output
case patchs of
Maybe [Revision]
Nothing -> FileStoreError -> IO FilePath
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO FileStoreError
NotFound
Just [] -> FileStoreError -> IO FilePath
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO FileStoreError
NotFound
Just (Revision
x:[Revision]
_) -> FilePath -> IO FilePath
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (FilePath -> IO FilePath) -> FilePath -> IO FilePath
forall a b. (a -> b) -> a -> b
$ Revision -> FilePath
revId Revision
x
darcsRetrieve :: Contents a
=> FilePath
-> FilePath
-> Maybe RevisionId
-> IO a
darcsRetrieve :: forall a.
Contents a =>
FilePath -> FilePath -> Maybe FilePath -> IO a
darcsRetrieve FilePath
repo FilePath
name Maybe FilePath
mbId = do
let opts :: [FilePath]
opts = case Maybe FilePath
mbId of
Maybe FilePath
Nothing -> [FilePath
"contents", FilePath
name]
Just FilePath
revid -> [FilePath
"contents", FilePath
"--match=hash " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
revid, FilePath
name]
(status, err, output) <- FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"show" [FilePath]
opts
if B.null output
then do
(_, _, out) <- runDarcsCommand repo "show" (["files", "--no-directories"] ++ opts)
if B.null out || null (filter (== name) . getNames $ output)
then throwIO NotFound
else return ()
else return ()
if status == ExitSuccess
then return $ fromByteString output
else throwIO $ UnknownError $ "Error in darcs query contents:\n" ++ err
getNames :: B.ByteString -> [String]
getNames :: ByteString -> [FilePath]
getNames = (FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> FilePath -> FilePath
forall a. Int -> [a] -> [a]
drop Int
2) ([FilePath] -> [FilePath])
-> (ByteString -> [FilePath]) -> ByteString -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath]
lines (FilePath -> [FilePath])
-> (ByteString -> FilePath) -> ByteString -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> FilePath
toString
darcsIndex :: FilePath ->IO [FilePath]
darcsIndex :: FilePath -> IO [FilePath]
darcsIndex FilePath
repo = FilePath -> IO [FilePath] -> IO [FilePath]
forall a. FilePath -> IO a -> IO a
withVerifyDir FilePath
repo (IO [FilePath] -> IO [FilePath]) -> IO [FilePath] -> IO [FilePath]
forall a b. (a -> b) -> a -> b
$ do
(status, _errOutput, output) <- FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"show" [FilePath
"files",FilePath
"--no-directories"]
if status == ExitSuccess
then return . getNames $ output
else return []
darcsDirectory :: FilePath -> FilePath -> IO [Resource]
darcsDirectory :: FilePath -> FilePath -> IO [Resource]
darcsDirectory FilePath
repo FilePath
dir = FilePath -> IO [Resource] -> IO [Resource]
forall a. FilePath -> IO a -> IO a
withVerifyDir (FilePath
repo FilePath -> FilePath -> FilePath
</> FilePath
dir) (IO [Resource] -> IO [Resource]) -> IO [Resource] -> IO [Resource]
forall a b. (a -> b) -> a -> b
$ do
let dir' :: FilePath
dir' = if FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
dir then FilePath
"" else FilePath -> FilePath
addTrailingPathSeparator FilePath
dir
(status1, _errOutput1, output1) <- FilePath
-> FilePath -> [FilePath] -> IO (ExitCode, FilePath, ByteString)
runDarcsCommand FilePath
repo FilePath
"show" [FilePath
"files",FilePath
"--no-directories"]
(status2, _errOutput2, output2) <- runDarcsCommand repo "show" ["files","--no-files"]
if status1 == ExitSuccess && status2 == ExitSuccess
then do
let files = FilePath -> [FilePath] -> [FilePath]
adhocParsing FilePath
dir' ([FilePath] -> [FilePath])
-> (ByteString -> [FilePath]) -> ByteString -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath]
lines (FilePath -> [FilePath])
-> (ByteString -> FilePath) -> ByteString -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> FilePath
toString (ByteString -> [FilePath]) -> ByteString -> [FilePath]
forall a b. (a -> b) -> a -> b
$ ByteString
output1
let dirs = FilePath -> [FilePath] -> [FilePath]
adhocParsing FilePath
dir' ([FilePath] -> [FilePath])
-> (ByteString -> [FilePath]) -> ByteString -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [FilePath] -> [FilePath]
forall a. Int -> [a] -> [a]
drop Int
1 ([FilePath] -> [FilePath])
-> (ByteString -> [FilePath]) -> ByteString -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath]
lines (FilePath -> [FilePath])
-> (ByteString -> FilePath) -> ByteString -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> FilePath
toString (ByteString -> [FilePath]) -> ByteString -> [FilePath]
forall a b. (a -> b) -> a -> b
$ ByteString
output2
let files' = (FilePath -> Resource) -> [FilePath] -> [Resource]
forall a b. (a -> b) -> [a] -> [b]
map FilePath -> Resource
FSFile ([FilePath] -> [Resource]) -> [FilePath] -> [Resource]
forall a b. (a -> b) -> a -> b
$ (FilePath -> Bool) -> [FilePath] -> [FilePath]
forall a. (a -> Bool) -> [a] -> [a]
filter (Char
'/' Char -> FilePath -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem`) [FilePath]
files
let dirs' = (FilePath -> Resource) -> [FilePath] -> [Resource]
forall a b. (a -> b) -> [a] -> [b]
map FilePath -> Resource
FSDirectory ([FilePath] -> [Resource]) -> [FilePath] -> [Resource]
forall a b. (a -> b) -> a -> b
$ (FilePath -> Bool) -> [FilePath] -> [FilePath]
forall a. (a -> Bool) -> [a] -> [a]
filter (Char
'/' Char -> FilePath -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem`) [FilePath]
dirs
return $ sort (files' ++ dirs')
else return []
where adhocParsing :: FilePath -> [FilePath] -> [FilePath]
adhocParsing FilePath
d = (FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> FilePath -> FilePath
forall a. Int -> [a] -> [a]
drop (Int -> FilePath -> FilePath) -> Int -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length FilePath
d Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) ([FilePath] -> [FilePath])
-> ([FilePath] -> [FilePath]) -> [FilePath] -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath -> Bool) -> [FilePath] -> [FilePath]
forall a. (a -> Bool) -> [a] -> [a]
filter ((FilePath
"." FilePath -> FilePath -> FilePath
</> FilePath
d) FilePath -> FilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf`)
darcsSearch :: FilePath -> SearchQuery -> IO [SearchMatch]
darcsSearch :: FilePath -> SearchQuery -> IO [SearchMatch]
darcsSearch = (FilePath -> IO [FilePath])
-> FilePath -> SearchQuery -> IO [SearchMatch]
grepSearchRepo FilePath -> IO [FilePath]
darcsIndex