serokell/importify

View on GitHub
src/Importify/Cabal/Module.hs

Summary

Maintainability
Test Coverage
-- | Functions to operate with @exposed-modules@ and @other-modules@
-- parts of .cabal file.

module Importify.Cabal.Module
       ( modulePaths
       , splitOnExposedAndOther
       ) where

import           Universum                       hiding (fromString)

import           Data.List                       (partition)
import           Distribution.ModuleName         (ModuleName, fromString, toFilePath)
import qualified Distribution.ModuleName         as Cabal
import           Distribution.PackageDescription (BuildInfo (..), Library (..))
import qualified Language.Haskell.Exts           as HSE
import           Path                            (Abs, Dir, File, Path, Rel, parseRelDir,
                                                  parseRelFile, (</>))
import           Path.IO                         (doesFileExist)

import           Importify.Syntax                (getModuleTitle)

-- | Split list of modules into /exposed/ modules and /other/ modules for given library.
-- __First__ element of pair represents /exposed/ modules.
-- __Second__ element of paris represents /other/ modules.
splitOnExposedAndOther :: Library
                       -> [HSE.Module l]
                       -> ([HSE.Module l], [HSE.Module l])
splitOnExposedAndOther Library{..} =
    partition ((`elem` exposedModules) . Cabal.fromString . getModuleTitle)

-- | Returns list of absolute paths to all modules inside given target.
modulePaths :: Path Abs Dir
            -- ^ Absolute path to project directory
            -> BuildInfo
            -- ^ 'BuildInfo' for given target
            -> Either [ModuleName] FilePath
            -- ^ Modules for Library and path for others
            -> IO [Path Abs File]
modulePaths packagePath BuildInfo{..} extra = do
    let (cur, others) = partition (== ".") hsSourceDirs
    case (cur, others) of
        (_here,    []) -> collectModulesHere
        ([]   , paths) -> collectModulesThere paths
        (_here, paths) -> liftA2 (++) collectModulesHere (collectModulesThere paths)
  where
    modulesToPaths :: [ModuleName] -> IO [Path Rel File]
    modulesToPaths = mapM (parseRelFile . (++ ".hs") . toFilePath)

    targetModulePaths :: IO [Path Rel File]
    targetModulePaths = case extra of
        Left modules -> modulesToPaths $ otherModules ++ modules
        Right path   -> liftA2 (:) (parseRelFile path) (modulesToPaths otherModules)

    addDir :: Path Abs Dir -> [Path Rel File] -> [Path Abs File]
    addDir dir = map (dir </>)

    collectModulesHere :: IO [Path Abs File]
    collectModulesHere = do
        paths <- targetModulePaths
        let packagePaths = addDir packagePath paths
        keepExistingModules packagePaths

    collectModulesThere :: [FilePath] -> IO [Path Abs File]
    collectModulesThere dirs = do
        dirPaths <- mapM parseRelDir dirs
        modPaths <- targetModulePaths
        concatForM dirPaths $ \dir ->
          keepExistingModules $ addDir (packagePath </> dir) modPaths

    keepExistingModules :: [Path Abs File] -> IO [Path Abs File]
    keepExistingModules = filterM doesFileExist