{-# LANGUAGE CPP #-} module Language.Haskell.Source.Enumerator ( enumeratePath ) where import Conduit import Control.Applicative import Control.Monad import Data.List import Distribution.PackageDescription import qualified Distribution.Verbosity as Verbosity import System.Directory import System.FilePath #if MIN_VERSION_Cabal(2,2,0) import Distribution.PackageDescription.Parsec (readGenericPackageDescription) #elif MIN_VERSION_Cabal(2,0,0) import Distribution.PackageDescription.Parse (readGenericPackageDescription) #else import Distribution.PackageDescription.Parse (readPackageDescription) readGenericPackageDescription :: Verbosity.Verbosity -> FilePath -> IO GenericPackageDescription readGenericPackageDescription = readPackageDescription #endif enumeratePath :: FilePath -> ConduitT () FilePath IO () enumeratePath path = enumPath path .| mapC normalise enumPath :: FilePath -> ConduitT () FilePath IO () enumPath path = do isDirectory <- lift $ doesDirectoryExist path case isDirectory of True -> enumDirectory path False | hasCabalExtension path -> enumPackage path False | hasHaskellExtension path -> yield path False -> return () enumPackage :: FilePath -> ConduitT () FilePath IO () enumPackage cabalFile = readPackage cabalFile >>= expandPaths where readPackage = lift . readGenericPackageDescription Verbosity.silent expandPaths = mapM_ (enumPath . mkFull) . sourcePaths packageDir = dropFileName cabalFile mkFull = (packageDir ) enumDirectory :: FilePath -> ConduitT () FilePath IO () enumDirectory path = do contents <- lift $ getDirectoryContentFullPaths path cabalFiles <- lift $ filterM isCabalFile contents if null cabalFiles then mapM_ enumPath contents else mapM_ enumPackage cabalFiles getDirectoryContentFullPaths :: FilePath -> IO [FilePath] getDirectoryContentFullPaths path = mkFull . notHidden . notMeta <$> getDirectoryContents path where mkFull = map (path ) notHidden = filter (not . isPrefixOf ".") notMeta = (\\ [".", ".."]) isCabalFile :: FilePath -> IO Bool isCabalFile path = return (hasCabalExtension path) <&&> doesFileExist path hasCabalExtension :: FilePath -> Bool hasCabalExtension path = ".cabal" `isSuffixOf` path hasHaskellExtension :: FilePath -> Bool hasHaskellExtension path = ".hs" `isSuffixOf` path || ".lhs" `isSuffixOf` path sourcePaths :: GenericPackageDescription -> [FilePath] sourcePaths pkg = nub $ concatMap ($ pkg) pathExtractors where pathExtractors = [ maybe [] (hsSourceDirs . libBuildInfo . condTreeData) . condLibrary , concatMap (hsSourceDirs . buildInfo . condTreeData . snd) . condExecutables , concatMap (hsSourceDirs . testBuildInfo . condTreeData . snd) . condTestSuites , concatMap (hsSourceDirs . benchmarkBuildInfo . condTreeData . snd) . condBenchmarks ] (<&&>) :: Applicative f => f Bool -> f Bool -> f Bool (<&&>) = liftA2 (&&) infixr 3 <&&> -- same as (&&)