-- |
-- License: GPL-3.0-or-later
-- Copyright: Oleg Grenrus
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FunctionalDependencies     #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables        #-}
module CabalFmt.Monad (
    -- * Monad class
    MonadCabalFmt (..),
    getFiles,
    Contents (..),
    -- * Pure implementation
    CabalFmt,
    runCabalFmt,
    -- * IO implementation
    CabalFmtIO,
    runCabalFmtIO,
    ) where

import Control.Exception
       (IOException, catch, displayException, throwIO, try)
import Control.Monad          (when)
import Control.Monad.Except   (MonadError (..))
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Reader   (MonadReader (..), ReaderT (..), asks, runReaderT)
import Control.Monad.Writer   (WriterT, runWriterT, tell)
import Data.Bifunctor         (first)
import Data.List              (isPrefixOf, stripPrefix)
import Data.Maybe             (mapMaybe)
import System.Exit            (exitFailure)
import System.FilePath        (pathSeparator, (</>))
import System.IO              (hPutStrLn, stderr)

import qualified Data.ByteString  as BS
import qualified Data.Map         as Map
import qualified System.Directory as D

import CabalFmt.Error
import CabalFmt.Options

-------------------------------------------------------------------------------
-- Class
-------------------------------------------------------------------------------

-- | @cabal-fmt@ interface.
--
-- * reader of 'Options'
-- * errors of 'Error'
-- * can list directories
--
class (HasOptions r, MonadReader r m, MonadError Error m) => MonadCabalFmt r m | m -> r where
    listDirectory      :: FilePath -> m [FilePath]
    doesDirectoryExist :: FilePath -> m Bool

    readFileBS         :: FilePath -> m Contents

    displayWarning     :: String -> m ()

data Contents
    = Contents BS.ByteString
    | NoIO
    | IOError String

-------------------------------------------------------------------------------
-- Pure
-------------------------------------------------------------------------------

-- | Pure 'MonadCabalFmt'.
--
-- 'listDirectory' always return empty list.
--
newtype CabalFmt a = CabalFmt { forall a.
CabalFmt a
-> ReaderT
     (Options, Map FilePath ByteString)
     (WriterT [FilePath] (Either Error))
     a
unCabalFmt :: ReaderT (Options, Map.Map FilePath BS.ByteString) (WriterT [String] (Either Error)) a }
  deriving newtype (forall a b. a -> CabalFmt b -> CabalFmt a
forall a b. (a -> b) -> CabalFmt a -> CabalFmt b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> CabalFmt b -> CabalFmt a
$c<$ :: forall a b. a -> CabalFmt b -> CabalFmt a
fmap :: forall a b. (a -> b) -> CabalFmt a -> CabalFmt b
$cfmap :: forall a b. (a -> b) -> CabalFmt a -> CabalFmt b
Functor, Functor CabalFmt
forall a. a -> CabalFmt a
forall a b. CabalFmt a -> CabalFmt b -> CabalFmt a
forall a b. CabalFmt a -> CabalFmt b -> CabalFmt b
forall a b. CabalFmt (a -> b) -> CabalFmt a -> CabalFmt b
forall a b c.
(a -> b -> c) -> CabalFmt a -> CabalFmt b -> CabalFmt c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b. CabalFmt a -> CabalFmt b -> CabalFmt a
$c<* :: forall a b. CabalFmt a -> CabalFmt b -> CabalFmt a
*> :: forall a b. CabalFmt a -> CabalFmt b -> CabalFmt b
$c*> :: forall a b. CabalFmt a -> CabalFmt b -> CabalFmt b
liftA2 :: forall a b c.
(a -> b -> c) -> CabalFmt a -> CabalFmt b -> CabalFmt c
$cliftA2 :: forall a b c.
(a -> b -> c) -> CabalFmt a -> CabalFmt b -> CabalFmt c
<*> :: forall a b. CabalFmt (a -> b) -> CabalFmt a -> CabalFmt b
$c<*> :: forall a b. CabalFmt (a -> b) -> CabalFmt a -> CabalFmt b
pure :: forall a. a -> CabalFmt a
$cpure :: forall a. a -> CabalFmt a
Applicative, Applicative CabalFmt
forall a. a -> CabalFmt a
forall a b. CabalFmt a -> CabalFmt b -> CabalFmt b
forall a b. CabalFmt a -> (a -> CabalFmt b) -> CabalFmt b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: forall a. a -> CabalFmt a
$creturn :: forall a. a -> CabalFmt a
>> :: forall a b. CabalFmt a -> CabalFmt b -> CabalFmt b
$c>> :: forall a b. CabalFmt a -> CabalFmt b -> CabalFmt b
>>= :: forall a b. CabalFmt a -> (a -> CabalFmt b) -> CabalFmt b
$c>>= :: forall a b. CabalFmt a -> (a -> CabalFmt b) -> CabalFmt b
Monad, MonadError Error)

instance MonadReader Options CabalFmt where
    ask :: CabalFmt Options
ask = forall a.
ReaderT
  (Options, Map FilePath ByteString)
  (WriterT [FilePath] (Either Error))
  a
-> CabalFmt a
CabalFmt forall a b. (a -> b) -> a -> b
$ forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks forall a b. (a, b) -> a
fst

    local :: forall a. (Options -> Options) -> CabalFmt a -> CabalFmt a
local Options -> Options
f (CabalFmt ReaderT
  (Options, Map FilePath ByteString)
  (WriterT [FilePath] (Either Error))
  a
m) = forall a.
ReaderT
  (Options, Map FilePath ByteString)
  (WriterT [FilePath] (Either Error))
  a
-> CabalFmt a
CabalFmt forall a b. (a -> b) -> a -> b
$ forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local (forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first Options -> Options
f) ReaderT
  (Options, Map FilePath ByteString)
  (WriterT [FilePath] (Either Error))
  a
m

instance MonadCabalFmt Options CabalFmt where
    listDirectory :: FilePath -> CabalFmt [FilePath]
listDirectory FilePath
dir = forall a.
ReaderT
  (Options, Map FilePath ByteString)
  (WriterT [FilePath] (Either Error))
  a
-> CabalFmt a
CabalFmt forall a b. (a -> b) -> a -> b
$ do
        Map FilePath ByteString
files <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks forall a b. (a, b) -> b
snd
        forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe FilePath -> Maybe FilePath
f (forall k a. Map k a -> [k]
Map.keys Map FilePath ByteString
files)
      where
        f :: FilePath -> Maybe FilePath
        f :: FilePath -> Maybe FilePath
f FilePath
fp = do
            FilePath
rest <- forall a. Eq a => [a] -> [a] -> Maybe [a]
stripPrefix (FilePath
dir forall a. [a] -> [a] -> [a]
++ [Char
pathSeparator]) FilePath
fp
            forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
/= Char
pathSeparator) FilePath
rest

    doesDirectoryExist :: FilePath -> CabalFmt Bool
doesDirectoryExist FilePath
dir = forall a.
ReaderT
  (Options, Map FilePath ByteString)
  (WriterT [FilePath] (Either Error))
  a
-> CabalFmt a
CabalFmt forall a b. (a -> b) -> a -> b
$ do
        Map FilePath ByteString
files <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks forall a b. (a, b) -> b
snd
        forall (m :: * -> *) a. Monad m => a -> m a
return (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall a. Eq a => [a] -> [a] -> Bool
isPrefixOf (FilePath
dir forall a. [a] -> [a] -> [a]
++ [Char
pathSeparator])) (forall k a. Map k a -> [k]
Map.keys Map FilePath ByteString
files))

    readFileBS :: FilePath -> CabalFmt Contents
readFileBS FilePath
p         = forall a.
ReaderT
  (Options, Map FilePath ByteString)
  (WriterT [FilePath] (Either Error))
  a
-> CabalFmt a
CabalFmt forall a b. (a -> b) -> a -> b
$ do
        Map FilePath ByteString
files <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks forall a b. (a, b) -> b
snd
        forall (m :: * -> *) a. Monad m => a -> m a
return (forall b a. b -> (a -> b) -> Maybe a -> b
maybe (FilePath -> Contents
IOError FilePath
"doesn't exist") ByteString -> Contents
Contents forall a b. (a -> b) -> a -> b
$ forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup FilePath
p Map FilePath ByteString
files)

    displayWarning :: FilePath -> CabalFmt ()
displayWarning FilePath
w     = do
        Bool
werror <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Options -> Bool
optError
        if Bool
werror
        then forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError forall a b. (a -> b) -> a -> b
$ FilePath -> Error
WarningError FilePath
w
        else forall a.
ReaderT
  (Options, Map FilePath ByteString)
  (WriterT [FilePath] (Either Error))
  a
-> CabalFmt a
CabalFmt forall a b. (a -> b) -> a -> b
$ forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [FilePath
w]

runCabalFmt
    :: Map.Map FilePath BS.ByteString -> Options
    -> CabalFmt a -> Either Error (a, [String])
runCabalFmt :: forall a.
Map FilePath ByteString
-> Options -> CabalFmt a -> Either Error (a, [FilePath])
runCabalFmt Map FilePath ByteString
files Options
opts CabalFmt a
m = forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
runWriterT (forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (forall a.
CabalFmt a
-> ReaderT
     (Options, Map FilePath ByteString)
     (WriterT [FilePath] (Either Error))
     a
unCabalFmt CabalFmt a
m) (Options
opts, Map FilePath ByteString
files))

-------------------------------------------------------------------------------
-- IO
-------------------------------------------------------------------------------

-- | Options with root for directory traversals
data Options' = Options'
    { Options' -> Maybe FilePath
optRootDir :: Maybe FilePath
    , Options' -> Options
optOpt     :: Options
    }

instance HasOptions Options' where
    options :: forall (f :: * -> *). Functor f => LensLike' f Options' Options
options Options -> f Options
f (Options' Maybe FilePath
mfp Options
o) = Maybe FilePath -> Options -> Options'
Options' Maybe FilePath
mfp forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Options -> f Options
f Options
o

newtype CabalFmtIO a = CabalFmtIO { forall a. CabalFmtIO a -> ReaderT Options' IO a
unCabalFmtIO :: ReaderT Options' IO a }
  deriving newtype (forall a b. a -> CabalFmtIO b -> CabalFmtIO a
forall a b. (a -> b) -> CabalFmtIO a -> CabalFmtIO b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> CabalFmtIO b -> CabalFmtIO a
$c<$ :: forall a b. a -> CabalFmtIO b -> CabalFmtIO a
fmap :: forall a b. (a -> b) -> CabalFmtIO a -> CabalFmtIO b
$cfmap :: forall a b. (a -> b) -> CabalFmtIO a -> CabalFmtIO b
Functor, Functor CabalFmtIO
forall a. a -> CabalFmtIO a
forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO a
forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO b
forall a b. CabalFmtIO (a -> b) -> CabalFmtIO a -> CabalFmtIO b
forall a b c.
(a -> b -> c) -> CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO a
$c<* :: forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO a
*> :: forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO b
$c*> :: forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO b
liftA2 :: forall a b c.
(a -> b -> c) -> CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO c
$cliftA2 :: forall a b c.
(a -> b -> c) -> CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO c
<*> :: forall a b. CabalFmtIO (a -> b) -> CabalFmtIO a -> CabalFmtIO b
$c<*> :: forall a b. CabalFmtIO (a -> b) -> CabalFmtIO a -> CabalFmtIO b
pure :: forall a. a -> CabalFmtIO a
$cpure :: forall a. a -> CabalFmtIO a
Applicative, Applicative CabalFmtIO
forall a. a -> CabalFmtIO a
forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO b
forall a b. CabalFmtIO a -> (a -> CabalFmtIO b) -> CabalFmtIO b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: forall a. a -> CabalFmtIO a
$creturn :: forall a. a -> CabalFmtIO a
>> :: forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO b
$c>> :: forall a b. CabalFmtIO a -> CabalFmtIO b -> CabalFmtIO b
>>= :: forall a b. CabalFmtIO a -> (a -> CabalFmtIO b) -> CabalFmtIO b
$c>>= :: forall a b. CabalFmtIO a -> (a -> CabalFmtIO b) -> CabalFmtIO b
Monad, Monad CabalFmtIO
forall a. IO a -> CabalFmtIO a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
liftIO :: forall a. IO a -> CabalFmtIO a
$cliftIO :: forall a. IO a -> CabalFmtIO a
MonadIO, MonadReader Options')

instance MonadError Error CabalFmtIO where
    throwError :: forall a. Error -> CabalFmtIO a
throwError = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e a. Exception e => e -> IO a
throwIO
    catchError :: forall a. CabalFmtIO a -> (Error -> CabalFmtIO a) -> CabalFmtIO a
catchError CabalFmtIO a
m Error -> CabalFmtIO a
h = forall a. ReaderT Options' IO a -> CabalFmtIO a
CabalFmtIO forall a b. (a -> b) -> a -> b
$ forall r (m :: * -> *) a. (r -> m a) -> ReaderT r m a
ReaderT forall a b. (a -> b) -> a -> b
$ \Options'
r ->
        forall e a. Exception e => IO a -> (e -> IO a) -> IO a
catch (forall {a}. Options' -> CabalFmtIO a -> IO a
unCabalFmtIO' Options'
r CabalFmtIO a
m) (forall {a}. Options' -> CabalFmtIO a -> IO a
unCabalFmtIO' Options'
r forall b c a. (b -> c) -> (a -> b) -> a -> c
. Error -> CabalFmtIO a
h)
      where
        unCabalFmtIO' :: Options' -> CabalFmtIO a -> IO a
unCabalFmtIO' Options'
r CabalFmtIO a
m' = forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (forall a. CabalFmtIO a -> ReaderT Options' IO a
unCabalFmtIO CabalFmtIO a
m') Options'
r

instance MonadCabalFmt Options' CabalFmtIO where
    listDirectory :: FilePath -> CabalFmtIO [FilePath]
listDirectory FilePath
p = do
        Maybe FilePath
rd <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Options' -> Maybe FilePath
optRootDir
        case Maybe FilePath
rd of
            Maybe FilePath
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return []
            Just FilePath
d  -> forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO [FilePath]
D.listDirectory (FilePath
d FilePath -> FilePath -> FilePath
</> FilePath
p))
    doesDirectoryExist :: FilePath -> CabalFmtIO Bool
doesDirectoryExist FilePath
p = do
        Maybe FilePath
rd <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Options' -> Maybe FilePath
optRootDir
        case Maybe FilePath
rd of
            Maybe FilePath
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
            Just FilePath
d  -> forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO Bool
D.doesDirectoryExist (FilePath
d FilePath -> FilePath -> FilePath
</> FilePath
p))
    readFileBS :: FilePath -> CabalFmtIO Contents
readFileBS FilePath
p = do
        Maybe FilePath
rd <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Options' -> Maybe FilePath
optRootDir
        case Maybe FilePath
rd of
            Maybe FilePath
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return Contents
NoIO
            Just FilePath
d  -> forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ IO ByteString -> IO Contents
catchIOError forall a b. (a -> b) -> a -> b
$ FilePath -> IO ByteString
BS.readFile (FilePath
d FilePath -> FilePath -> FilePath
</> FilePath
p)
    displayWarning :: FilePath -> CabalFmtIO ()
displayWarning FilePath
w = do
        Bool
werror <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (Options -> Bool
optError forall b c a. (b -> c) -> (a -> b) -> a -> c
. Options' -> Options
optOpt)
        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
            Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr forall a b. (a -> b) -> a -> b
$ (if Bool
werror then FilePath
"ERROR: " else FilePath
"WARNING: ") forall a. [a] -> [a] -> [a]
++ FilePath
w
            forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
werror forall a. IO a
exitFailure

catchIOError :: IO BS.ByteString -> IO Contents
catchIOError :: IO ByteString -> IO Contents
catchIOError IO ByteString
m = forall e a. Exception e => IO a -> (e -> IO a) -> IO a
catch (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Contents
Contents IO ByteString
m) IOException -> IO Contents
handler where
    handler :: IOException -> IO Contents
    handler :: IOException -> IO Contents
handler IOException
exc = forall (m :: * -> *) a. Monad m => a -> m a
return (FilePath -> Contents
IOError (forall e. Exception e => e -> FilePath
displayException IOException
exc))

runCabalFmtIO :: Maybe FilePath -> Options -> CabalFmtIO a -> IO (Either Error a)
runCabalFmtIO :: forall a.
Maybe FilePath -> Options -> CabalFmtIO a -> IO (Either Error a)
runCabalFmtIO Maybe FilePath
mfp Options
opts CabalFmtIO a
m = forall e a. Exception e => IO a -> IO (Either e a)
try forall a b. (a -> b) -> a -> b
$ forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (forall a. CabalFmtIO a -> ReaderT Options' IO a
unCabalFmtIO CabalFmtIO a
m) (Maybe FilePath -> Options -> Options'
Options' Maybe FilePath
mfp Options
opts)

-------------------------------------------------------------------------------
-- Files
-------------------------------------------------------------------------------

getFiles :: MonadCabalFmt r m => FilePath -> m [FilePath]
getFiles :: forall r (m :: * -> *).
MonadCabalFmt r m =>
FilePath -> m [FilePath]
getFiles = forall (m :: * -> *) r.
MonadCabalFmt r m =>
(FilePath -> Bool) -> FilePath -> m [FilePath]
getDirectoryContentsRecursive' FilePath -> Bool
check where
    check :: FilePath -> Bool
check FilePath
"dist-newstyle" = Bool
False
    check (Char
'.' : FilePath
_)       = Bool
False
    check FilePath
_               = Bool
True

-- | List all the files in a directory and all subdirectories.
--
-- The order places files in sub-directories after all the files in their
-- parent directories. The list is generated lazily so is not well defined if
-- the source directory structure changes before the list is used.
--
-- /Note:/ From @Cabal@'s "Distribution.Simple.Utils"
getDirectoryContentsRecursive'
    :: forall m r. MonadCabalFmt r m
    => (FilePath -> Bool) -- ^ Check, whether to recurse
    -> FilePath           -- ^ top dir
    -> m [FilePath]
getDirectoryContentsRecursive' :: forall (m :: * -> *) r.
MonadCabalFmt r m =>
(FilePath -> Bool) -> FilePath -> m [FilePath]
getDirectoryContentsRecursive' FilePath -> Bool
ignore' FilePath
topdir = [FilePath] -> m [FilePath]
recurseDirectories [FilePath
""]
  where
    recurseDirectories :: [FilePath] -> m [FilePath]
    recurseDirectories :: [FilePath] -> m [FilePath]
recurseDirectories []         = forall (m :: * -> *) a. Monad m => a -> m a
return []
    recurseDirectories (FilePath
dir:[FilePath]
dirs) = do
      ([FilePath]
files, [FilePath]
dirs') <- forall {m :: * -> *} {r}.
MonadCabalFmt r m =>
[FilePath]
-> [FilePath] -> [FilePath] -> m ([FilePath], [FilePath])
collect [] [] forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall r (m :: * -> *).
MonadCabalFmt r m =>
FilePath -> m [FilePath]
listDirectory (FilePath
topdir FilePath -> FilePath -> FilePath
</> FilePath
dir)
      [FilePath]
files' <- [FilePath] -> m [FilePath]
recurseDirectories ([FilePath]
dirs' forall a. [a] -> [a] -> [a]
++ [FilePath]
dirs)
      forall (m :: * -> *) a. Monad m => a -> m a
return ([FilePath]
files forall a. [a] -> [a] -> [a]
++ [FilePath]
files')

      where
        collect :: [FilePath]
-> [FilePath] -> [FilePath] -> m ([FilePath], [FilePath])
collect [FilePath]
files [FilePath]
dirs' []              = forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. [a] -> [a]
reverse [FilePath]
files
                                                     ,forall a. [a] -> [a]
reverse [FilePath]
dirs')
        collect [FilePath]
files [FilePath]
dirs' (FilePath
entry:[FilePath]
entries) | FilePath -> Bool
ignore FilePath
entry
                                            = [FilePath]
-> [FilePath] -> [FilePath] -> m ([FilePath], [FilePath])
collect [FilePath]
files [FilePath]
dirs' [FilePath]
entries
        collect [FilePath]
files [FilePath]
dirs' (FilePath
entry:[FilePath]
entries) = do
          let dirEntry :: FilePath
dirEntry = FilePath
dir FilePath -> FilePath -> FilePath
</> FilePath
entry
          Bool
isDirectory <- forall r (m :: * -> *). MonadCabalFmt r m => FilePath -> m Bool
doesDirectoryExist (FilePath
topdir FilePath -> FilePath -> FilePath
</> FilePath
dirEntry)
          if Bool
isDirectory
            then [FilePath]
-> [FilePath] -> [FilePath] -> m ([FilePath], [FilePath])
collect [FilePath]
files (FilePath
dirEntryforall a. a -> [a] -> [a]
:[FilePath]
dirs') [FilePath]
entries
            else [FilePath]
-> [FilePath] -> [FilePath] -> m ([FilePath], [FilePath])
collect (FilePath
dirEntryforall a. a -> [a] -> [a]
:[FilePath]
files) [FilePath]
dirs' [FilePath]
entries

        ignore :: FilePath -> Bool
ignore [Char
'.']      = Bool
True
        ignore [Char
'.', Char
'.'] = Bool
True
        ignore FilePath
x          = Bool -> Bool
not (FilePath -> Bool
ignore' FilePath
x)