{-|
License : GPL-2

A set of functions to identify and find Darcs repositories
from a given @URL@ or a given filesystem path.
-}

module Darcs.Repository.Identify
    ( maybeIdentifyRepository
    , identifyRepository
    , identifyRepositoryFor
    , IdentifyRepo(..)
    , findRepository
    , amInRepository
    , amNotInRepository
    , amInHashedRepository
    , seekRepo
    , findAllReposInDir
    ) where

import Prelude ()
import Darcs.Prelude

import Control.Monad ( forM )
import Darcs.Repository.Format ( tryIdentifyRepoFormat
                               , readProblem
                               , transferProblem
                               )
import System.Directory ( doesDirectoryExist
                        , setCurrentDirectory
                        , createDirectoryIfMissing
                        , doesFileExist
                        , getDirectoryContents
                        )
import System.FilePath.Posix ( (</>) )
import System.IO.Error ( catchIOError )
import Data.Maybe ( fromMaybe )

import Darcs.Repository.Old ( oldRepoFailMsg )
import Darcs.Repository.Flags ( UseCache(..), WorkRepo (..) )
import Darcs.Util.Path
    ( toFilePath
    , ioAbsoluteOrRemote
    , toPath
    )
import Darcs.Util.Exception ( catchall )
import Darcs.Util.URL ( isValidLocalPath )
import Darcs.Util.Workaround
    ( getCurrentDirectory
    )
import Darcs.Repository.Prefs ( getCaches )
import Darcs.Repository.InternalTypes( Repository
                                     , PristineType(..)
                                     , mkRepo
                                     , repoFormat
                                     , repoPristineType
                                     )
import Darcs.Util.Global ( darcsdir )

import System.Mem( performGC )

-- | The status of a given directory: is it a darcs repository?
data IdentifyRepo rt p wR wU wT
    = BadRepository String -- ^ looks like a repository with some error
    | NonRepository String -- ^ safest guess
    | GoodRepository (Repository rt p wR wU wT)

-- | Tries to identify the repository in a given directory
maybeIdentifyRepository :: UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
maybeIdentifyRepository :: UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
maybeIdentifyRepository useCache :: UseCache
useCache "." =
    do Bool
darcs <- String -> IO Bool
doesDirectoryExist String
darcsdir
       if Bool -> Bool
not Bool
darcs
        then IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> IdentifyRepo rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
String -> IdentifyRepo rt p wR wU wT
NonRepository (String -> IdentifyRepo rt p wR wU wT)
-> String -> IdentifyRepo rt p wR wU wT
forall a b. (a -> b) -> a -> b
$ "Missing " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
darcsdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ " directory")
        else do
        Either String RepoFormat
repoFormatOrError <- String -> IO (Either String RepoFormat)
tryIdentifyRepoFormat "."
        String
here <- AbsoluteOrRemotePath -> String
forall a. FilePathOrURL a => a -> String
toPath (AbsoluteOrRemotePath -> String)
-> IO AbsoluteOrRemotePath -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` String -> IO AbsoluteOrRemotePath
ioAbsoluteOrRemote "."
        case Either String RepoFormat
repoFormatOrError of
          Left err :: String
err -> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall (m :: * -> *) a. Monad m => a -> m a
return (IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT))
-> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall a b. (a -> b) -> a -> b
$ String -> IdentifyRepo rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
String -> IdentifyRepo rt p wR wU wT
NonRepository String
err
          Right rf :: RepoFormat
rf ->
              case RepoFormat -> Maybe String
readProblem RepoFormat
rf of
              Just err :: String
err -> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall (m :: * -> *) a. Monad m => a -> m a
return (IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT))
-> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall a b. (a -> b) -> a -> b
$ String -> IdentifyRepo rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
String -> IdentifyRepo rt p wR wU wT
BadRepository String
err
              Nothing -> do PristineType
pris <- IO PristineType
identifyPristine
                            Cache
cs <- UseCache -> String -> IO Cache
getCaches UseCache
useCache String
here
                            IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall (m :: * -> *) a. Monad m => a -> m a
return (IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT))
-> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall a b. (a -> b) -> a -> b
$ Repository rt p wR wU wT -> IdentifyRepo rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
Repository rt p wR wU wT -> IdentifyRepo rt p wR wU wT
GoodRepository (Repository rt p wR wU wT -> IdentifyRepo rt p wR wU wT)
-> Repository rt p wR wU wT -> IdentifyRepo rt p wR wU wT
forall a b. (a -> b) -> a -> b
$ String
-> RepoFormat -> PristineType -> Cache -> Repository rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
String
-> RepoFormat -> PristineType -> Cache -> Repository rt p wR wU wT
mkRepo String
here RepoFormat
rf PristineType
pris Cache
cs
maybeIdentifyRepository useCache :: UseCache
useCache url' :: String
url' =
 do String
url <- AbsoluteOrRemotePath -> String
forall a. FilePathOrURL a => a -> String
toPath (AbsoluteOrRemotePath -> String)
-> IO AbsoluteOrRemotePath -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` String -> IO AbsoluteOrRemotePath
ioAbsoluteOrRemote String
url'
    Either String RepoFormat
repoFormatOrError <- String -> IO (Either String RepoFormat)
tryIdentifyRepoFormat String
url
    case Either String RepoFormat
repoFormatOrError of
      Left e :: String
e -> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall (m :: * -> *) a. Monad m => a -> m a
return (IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT))
-> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall a b. (a -> b) -> a -> b
$ String -> IdentifyRepo rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
String -> IdentifyRepo rt p wR wU wT
NonRepository String
e
      Right rf :: RepoFormat
rf -> case RepoFormat -> Maybe String
readProblem RepoFormat
rf of
                  Just err :: String
err -> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall (m :: * -> *) a. Monad m => a -> m a
return (IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT))
-> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall a b. (a -> b) -> a -> b
$ String -> IdentifyRepo rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
String -> IdentifyRepo rt p wR wU wT
BadRepository String
err
                  Nothing ->  do Cache
cs <- UseCache -> String -> IO Cache
getCaches UseCache
useCache String
url
                                 IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall (m :: * -> *) a. Monad m => a -> m a
return (IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT))
-> IdentifyRepo rt p wR wU wT -> IO (IdentifyRepo rt p wR wU wT)
forall a b. (a -> b) -> a -> b
$ Repository rt p wR wU wT -> IdentifyRepo rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
Repository rt p wR wU wT -> IdentifyRepo rt p wR wU wT
GoodRepository (Repository rt p wR wU wT -> IdentifyRepo rt p wR wU wT)
-> Repository rt p wR wU wT -> IdentifyRepo rt p wR wU wT
forall a b. (a -> b) -> a -> b
$ String
-> RepoFormat -> PristineType -> Cache -> Repository rt p wR wU wT
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
String
-> RepoFormat -> PristineType -> Cache -> Repository rt p wR wU wT
mkRepo String
url RepoFormat
rf PristineType
NoPristine Cache
cs

identifyPristine :: IO PristineType
identifyPristine :: IO PristineType
identifyPristine =
    do Bool
pristine <- String -> IO Bool
doesDirectoryExist (String -> IO Bool) -> String -> IO Bool
forall a b. (a -> b) -> a -> b
$ String
darcsdirString -> String -> String
forall a. [a] -> [a] -> [a]
++"/pristine"
       Bool
current  <- String -> IO Bool
doesDirectoryExist (String -> IO Bool) -> String -> IO Bool
forall a b. (a -> b) -> a -> b
$ String
darcsdirString -> String -> String
forall a. [a] -> [a] -> [a]
++"/current"
       Bool
hashinv  <- String -> IO Bool
doesFileExist      (String -> IO Bool) -> String -> IO Bool
forall a b. (a -> b) -> a -> b
$ String
darcsdirString -> String -> String
forall a. [a] -> [a] -> [a]
++"/hashed_inventory"
       case (Bool
pristine Bool -> Bool -> Bool
|| Bool
current, Bool
hashinv) of
           (False, False) -> PristineType -> IO PristineType
forall (m :: * -> *) a. Monad m => a -> m a
return PristineType
NoPristine
           (True,  False) -> PristineType -> IO PristineType
forall (m :: * -> *) a. Monad m => a -> m a
return PristineType
PlainPristine
           (False, True ) -> PristineType -> IO PristineType
forall (m :: * -> *) a. Monad m => a -> m a
return PristineType
HashedPristine
           _ -> String -> IO PristineType
forall (m :: * -> *) a. MonadFail m => String -> m a
fail "Multiple pristine trees."

-- | identifyRepository identifies the repo at 'url'. Warning:
-- you have to know what kind of patches are found in that repo.
identifyRepository :: forall rt p wR wU wT. UseCache -> String
                           -> IO (Repository rt p wR wU wT)
identifyRepository :: UseCache -> String -> IO (Repository rt p wR wU wT)
identifyRepository useCache :: UseCache
useCache url :: String
url =
    do IdentifyRepo rt p wR wU wT
er <- UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
maybeIdentifyRepository UseCache
useCache String
url
       case IdentifyRepo rt p wR wU wT
er of
         BadRepository s :: String
s -> String -> IO (Repository rt p wR wU wT)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
s
         NonRepository s :: String
s -> String -> IO (Repository rt p wR wU wT)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
s
         GoodRepository r :: Repository rt p wR wU wT
r -> Repository rt p wR wU wT -> IO (Repository rt p wR wU wT)
forall (m :: * -> *) a. Monad m => a -> m a
return Repository rt p wR wU wT
r

-- | @identifyRepositoryFor repo url@ identifies (and returns) the repo at 'url',
-- but fails if it is not compatible for reading from and writing to.
identifyRepositoryFor :: forall rt p wR wU wT vR vU vT.
                         Repository rt p wR wU wT
                      -> UseCache
                      -> String
                      -> IO (Repository rt p vR vU vT)
identifyRepositoryFor :: Repository rt p wR wU wT
-> UseCache -> String -> IO (Repository rt p vR vU vT)
identifyRepositoryFor source :: Repository rt p wR wU wT
source useCache :: UseCache
useCache url :: String
url =
    do Repository rt p vR vU vT
target <- UseCache -> String -> IO (Repository rt p vR vU vT)
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
UseCache -> String -> IO (Repository rt p wR wU wT)
identifyRepository UseCache
useCache String
url
       case RepoFormat -> RepoFormat -> Maybe String
transferProblem (Repository rt p vR vU vT -> RepoFormat
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
Repository rt p wR wU wT -> RepoFormat
repoFormat Repository rt p vR vU vT
target) (Repository rt p wR wU wT -> RepoFormat
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
Repository rt p wR wU wT -> RepoFormat
repoFormat Repository rt p wR wU wT
source) of
         Just e :: String
e -> String -> IO (Repository rt p vR vU vT)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO (Repository rt p vR vU vT))
-> String -> IO (Repository rt p vR vU vT)
forall a b. (a -> b) -> a -> b
$ "Incompatibility with repository " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
url String -> String -> String
forall a. [a] -> [a] -> [a]
++ ":\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
e
         Nothing -> Repository rt p vR vU vT -> IO (Repository rt p vR vU vT)
forall (m :: * -> *) a. Monad m => a -> m a
return Repository rt p vR vU vT
target

amInRepository :: WorkRepo -> IO (Either String ())
amInRepository :: WorkRepo -> IO (Either String ())
amInRepository (WorkRepoDir d :: String
d) =
  do
    String -> IO ()
setCurrentDirectory String
d
    IdentifyRepo Any Any Any Any Any
status <- UseCache -> String -> IO (IdentifyRepo Any Any Any Any Any)
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
maybeIdentifyRepository UseCache
YesUseCache "."
    case IdentifyRepo Any Any Any Any Any
status of
      GoodRepository _ -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (() -> Either String ()
forall a b. b -> Either a b
Right ())
      BadRepository  e :: String
e -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ()
forall a b. a -> Either a b
Left (String -> Either String ()) -> String -> Either String ()
forall a b. (a -> b) -> a -> b
$ "While " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
d String -> String -> String
forall a. [a] -> [a] -> [a]
++ " looks like a repository directory, we have a problem with it:\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
e)
      NonRepository  _ -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ()
forall a b. a -> Either a b
Left "You need to be in a repository directory to run this command.")
  IO (Either String ())
-> (IOError -> IO (Either String ())) -> IO (Either String ())
forall a. IO a -> (IOError -> IO a) -> IO a
`catchIOError`
    \e :: IOError
e -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ()
forall a b. a -> Either a b
Left (IOError -> String
forall a. Show a => a -> String
show IOError
e))
  
amInRepository _ =
  Either String () -> Maybe (Either String ()) -> Either String ()
forall a. a -> Maybe a -> a
fromMaybe (String -> Either String ()
forall a b. a -> Either a b
Left "You need to be in a repository directory to run this command.") (Maybe (Either String ()) -> Either String ())
-> IO (Maybe (Either String ())) -> IO (Either String ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO (Maybe (Either String ()))
seekRepo

amInHashedRepository :: WorkRepo -> IO (Either String ())
amInHashedRepository :: WorkRepo -> IO (Either String ())
amInHashedRepository wd :: WorkRepo
wd
 = do Either String ()
inrepo <- WorkRepo -> IO (Either String ())
amInRepository WorkRepo
wd
      case Either String ()
inrepo of
       Right _ -> do PristineType
pristine <- IO PristineType
identifyPristine
                     case PristineType
pristine of
                       HashedPristine -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (() -> Either String ()
forall a b. b -> Either a b
Right ())
                       _ -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ()
forall a b. a -> Either a b
Left String
oldRepoFailMsg)
       left :: Either String ()
left    -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return Either String ()
left

-- | hunt upwards for the darcs repository
-- This keeps changing up one parent directory, testing at each
-- step if the current directory is a repository or not.  $
-- The result is:
--   Nothing, if no repository found
--   Just (Left errorMessage), if bad repository found
--   Just (Right ()), if good repository found.
-- WARNING this changes the current directory for good if matchFn succeeds
seekRepo :: IO (Maybe (Either String ()))
seekRepo :: IO (Maybe (Either String ()))
seekRepo = IO String
getCurrentDirectory IO String
-> (String -> IO (Maybe (Either String ())))
-> IO (Maybe (Either String ()))
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> IO (Maybe (Either String ()))
helper where
   helper :: String -> IO (Maybe (Either String ()))
helper startpwd :: String
startpwd = do
    IdentifyRepo Any Any Any Any Any
status <- UseCache -> String -> IO (IdentifyRepo Any Any Any Any Any)
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
maybeIdentifyRepository UseCache
YesUseCache "."
    case IdentifyRepo Any Any Any Any Any
status of
      GoodRepository _ -> Maybe (Either String ()) -> IO (Maybe (Either String ()))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (Either String ()) -> IO (Maybe (Either String ())))
-> (Either String () -> Maybe (Either String ()))
-> Either String ()
-> IO (Maybe (Either String ()))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either String () -> Maybe (Either String ())
forall a. a -> Maybe a
Just (Either String () -> IO (Maybe (Either String ())))
-> Either String () -> IO (Maybe (Either String ()))
forall a b. (a -> b) -> a -> b
$ () -> Either String ()
forall a b. b -> Either a b
Right ()
      BadRepository e :: String
e  -> Maybe (Either String ()) -> IO (Maybe (Either String ()))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (Either String ()) -> IO (Maybe (Either String ())))
-> (Either String () -> Maybe (Either String ()))
-> Either String ()
-> IO (Maybe (Either String ()))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either String () -> Maybe (Either String ())
forall a. a -> Maybe a
Just (Either String () -> IO (Maybe (Either String ())))
-> Either String () -> IO (Maybe (Either String ()))
forall a b. (a -> b) -> a -> b
$ String -> Either String ()
forall a b. a -> Either a b
Left String
e
      NonRepository _ ->
            do String
cd <- String -> String
forall a. FilePathLike a => a -> String
toFilePath (String -> String) -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` IO String
getCurrentDirectory
               String -> IO ()
setCurrentDirectory ".."
               String
cd' <- String -> String
forall a. FilePathLike a => a -> String
toFilePath (String -> String) -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` IO String
getCurrentDirectory
               if String
cd' String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String
cd
                  then String -> IO (Maybe (Either String ()))
helper String
startpwd
                  else do String -> IO ()
setCurrentDirectory String
startpwd
                          Maybe (Either String ()) -> IO (Maybe (Either String ()))
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (Either String ())
forall a. Maybe a
Nothing

-- The performGC in this function is a workaround for a library/GHC bug,
-- http://hackage.haskell.org/trac/ghc/ticket/2924 -- (doesn't seem to be a
-- problem on fast machines, but virtual ones trip this from time to time)
amNotInRepository :: WorkRepo -> IO (Either String ())
amNotInRepository :: WorkRepo -> IO (Either String ())
amNotInRepository (WorkRepoDir d :: String
d) = do
    Bool -> String -> IO ()
createDirectoryIfMissing Bool
False String
d
       IO () -> IO () -> IO ()
forall a. IO a -> IO a -> IO a
`catchall` (IO ()
performGC IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Bool -> String -> IO ()
createDirectoryIfMissing Bool
False String
d)
    -- note that the above could always fail
    String -> IO ()
setCurrentDirectory String
d
    WorkRepo -> IO (Either String ())
amNotInRepository WorkRepo
WorkRepoCurrentDir
amNotInRepository _ = do
       IdentifyRepo Any Any Any Any Any
status <- UseCache -> String -> IO (IdentifyRepo Any Any Any Any Any)
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
maybeIdentifyRepository UseCache
YesUseCache "."
       case IdentifyRepo Any Any Any Any Any
status of
         GoodRepository _ -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ()
forall a b. a -> Either a b
Left "You may not run this command in a repository.")
         BadRepository e :: String
e  -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ()
forall a b. a -> Either a b
Left (String -> Either String ()) -> String -> Either String ()
forall a b. (a -> b) -> a -> b
$ "You may not run this command in a repository.\nBy the way, we have a problem with it:\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
e)
         NonRepository _  -> Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (() -> Either String ()
forall a b. b -> Either a b
Right ())

findRepository :: WorkRepo -> IO (Either String ())
findRepository :: WorkRepo -> IO (Either String ())
findRepository workrepo :: WorkRepo
workrepo =
  case WorkRepo
workrepo of
    WorkRepoPossibleURL d :: String
d | String -> Bool
isValidLocalPath String
d -> do
      String -> IO ()
setCurrentDirectory String
d
      WorkRepo -> IO (Either String ())
findRepository WorkRepo
WorkRepoCurrentDir
    WorkRepoDir d :: String
d -> do
      String -> IO ()
setCurrentDirectory String
d
      WorkRepo -> IO (Either String ())
findRepository WorkRepo
WorkRepoCurrentDir
    _ -> Either String () -> Maybe (Either String ()) -> Either String ()
forall a. a -> Maybe a -> a
fromMaybe (() -> Either String ()
forall a b. b -> Either a b
Right ()) (Maybe (Either String ()) -> Either String ())
-> IO (Maybe (Either String ())) -> IO (Either String ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO (Maybe (Either String ()))
seekRepo
  IO (Either String ())
-> (IOError -> IO (Either String ())) -> IO (Either String ())
forall a. IO a -> (IOError -> IO a) -> IO a
`catchIOError` \e :: IOError
e ->
    Either String () -> IO (Either String ())
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ()
forall a b. a -> Either a b
Left (IOError -> String
forall a. Show a => a -> String
show IOError
e))

-- | @findAllReposInDir topDir@ returns all paths to repositories under @topDir@.
findAllReposInDir :: FilePath -> IO [FilePath]
findAllReposInDir :: String -> IO [String]
findAllReposInDir topDir :: String
topDir = do
  Bool
isDir <- String -> IO Bool
doesDirectoryExist String
topDir
  if Bool
isDir
    then do
      IdentifyRepo Any Any Any Any Any
status <- UseCache -> String -> IO (IdentifyRepo Any Any Any Any Any)
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
maybeIdentifyRepository UseCache
NoUseCache String
topDir
      case IdentifyRepo Any Any Any Any Any
status of
        GoodRepository repo :: Repository Any Any Any Any Any
repo
          | PristineType
HashedPristine <- Repository Any Any Any Any Any -> PristineType
forall (rt :: RepoType) (p :: * -> * -> *) wR wU wT.
Repository rt p wR wU wT -> PristineType
repoPristineType Repository Any Any Any Any Any
repo -> [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return [String
topDir]
          | Bool
otherwise -> [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return [] -- old fashioned or broken repo
        _             -> String -> IO [String]
getRecursiveDarcsRepos' String
topDir
    else [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return []
  where
    getRecursiveDarcsRepos' :: String -> IO [String]
getRecursiveDarcsRepos' d :: String
d = do
      [String]
names <- String -> IO [String]
getDirectoryContents String
d
      let properNames :: [String]
properNames = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (\x :: String
x -> String -> Char
forall a. [a] -> a
head String
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= '.') [String]
names
      [[String]]
paths <- [String] -> (String -> IO [String]) -> IO [[String]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [String]
properNames ((String -> IO [String]) -> IO [[String]])
-> (String -> IO [String]) -> IO [[String]]
forall a b. (a -> b) -> a -> b
$ \name :: String
name -> do
        let path :: String
path = String
d String -> String -> String
</> String
name
        String -> IO [String]
findAllReposInDir String
path
      [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return ([[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[String]]
paths)