cabal-install-3.16.0.0: src/Distribution/Client/Init/FlagExtractors.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PatternSynonyms #-}
module Distribution.Client.Init.FlagExtractors
( -- * Flag extractors
getPackageDir
, getSimpleProject
, getMinimal
, getCabalVersion
, getCabalVersionNoPrompt
, getPackageName
, getVersion
, getLicense
, getAuthor
, getEmail
, getHomepage
, getSynopsis
, getCategory
, getExtraSrcFiles
, getExtraDocFiles
, getPackageType
, getMainFile
, getInitializeTestSuite
, getTestDirs
, getLanguage
, getNoComments
, getAppDirs
, getSrcDirs
, getExposedModules
, getBuildTools
, getDependencies
, getOtherExts
, getOverwrite
, getOtherModules
-- * Shared prompts
, simpleProjectPrompt
, initializeTestSuitePrompt
, packageTypePrompt
, testMainPrompt
, dependenciesPrompt
) where
import Distribution.Client.Compat.Prelude hiding (getLine, last, putStr, putStrLn)
import Prelude ()
import qualified Data.List.NonEmpty as NEL
import Distribution.CabalSpecVersion (CabalSpecVersion (..))
import Distribution.Client.Init.Defaults
import Distribution.Client.Init.Types
import Distribution.FieldGrammar.Newtypes (SpecLicense)
import Distribution.ModuleName (ModuleName)
import Distribution.Simple.Flag (flagElim)
import Distribution.Simple.Setup (Flag, flagToMaybe, fromFlagOrDefault, pattern Flag, pattern NoFlag)
import Distribution.Types.Dependency (Dependency (..))
import Distribution.Types.PackageName (PackageName)
import Distribution.Version (Version)
import qualified Data.Set as Set
import Distribution.Client.Init.Prompt
import Distribution.Client.Init.Utils
import Distribution.Simple.PackageIndex
import Language.Haskell.Extension (Extension (..), Language (..))
-- -------------------------------------------------------------------- --
-- Flag extraction
getPackageDir :: Interactive m => InitFlags -> m FilePath
getPackageDir = flagElim getCurrentDirectory return . packageDir
-- | Ask if a simple project with sensible defaults should be created.
getSimpleProject :: Interactive m => InitFlags -> m Bool -> m Bool
getSimpleProject flags = fromFlagOrPrompt (simpleProject flags)
-- | Extract minimal cabal file flag (implies nocomments)
getMinimal :: Interactive m => InitFlags -> m Bool
getMinimal = return . fromFlagOrDefault False . minimal
-- | Get the version of the cabal spec to use.
--
-- The spec version can be specified by the InitFlags cabalVersion field. If
-- none is specified then the user is prompted to pick from a list of
-- supported versions (see code below).
getCabalVersion :: Interactive m => InitFlags -> m CabalSpecVersion -> m CabalSpecVersion
getCabalVersion flags = fromFlagOrPrompt (cabalVersion flags)
getCabalVersionNoPrompt :: InitFlags -> CabalSpecVersion
getCabalVersionNoPrompt = fromFlagOrDefault defaultCabalVersion . cabalVersion
-- | Get the package name: use the package directory (supplied, or the current
-- directory by default) as a guess. It looks at the SourcePackageDb to avoid
-- using an existing package name.
getPackageName :: Interactive m => InitFlags -> m PackageName -> m PackageName
getPackageName flags = fromFlagOrPrompt (packageName flags)
-- | Package version: use 0.1.0.0 as a last resort, but try prompting the user
-- if possible.
getVersion :: Interactive m => InitFlags -> m Version -> m Version
getVersion flags = fromFlagOrPrompt (version flags)
-- | Choose a license for the package.
-- The license can come from Initflags (license field), if it is not present
-- then prompt the user from a predefined list of licenses.
getLicense :: Interactive m => InitFlags -> m SpecLicense -> m SpecLicense
getLicense flags = fromFlagOrPrompt (license flags)
-- | The author's name. Prompt, or try to guess from an existing
-- darcs repo.
getAuthor :: Interactive m => InitFlags -> m String -> m String
getAuthor flags = fromFlagOrPrompt (author flags)
-- | The author's email. Prompt, or try to guess from an existing
-- darcs repo.
getEmail :: Interactive m => InitFlags -> m String -> m String
getEmail flags = fromFlagOrPrompt (email flags)
-- | Prompt for a homepage URL for the package.
getHomepage :: Interactive m => InitFlags -> m String -> m String
getHomepage flags = fromFlagOrPrompt (homepage flags)
-- | Prompt for a project synopsis.
getSynopsis :: Interactive m => InitFlags -> m String -> m String
getSynopsis flags = fromFlagOrPrompt (synopsis flags)
-- | Prompt for a package category.
-- Note that it should be possible to do some smarter guessing here too, i.e.
-- look at the name of the top level source directory.
getCategory :: Interactive m => InitFlags -> m String -> m String
getCategory flags = fromFlagOrPrompt (category flags)
-- | Try to guess extra source files (don't prompt the user).
getExtraSrcFiles :: Interactive m => InitFlags -> m (Set String)
getExtraSrcFiles = pure . flagElim mempty Set.fromList . extraSrc
-- | Try to guess extra source files (don't prompt the user).
getExtraDocFiles :: Interactive m => InitFlags -> m (Maybe (Set String))
getExtraDocFiles =
pure
. Just
. flagElim (Set.singleton defaultChangelog) Set.fromList
. extraDoc
-- | Ask whether the project builds a library or executable.
getPackageType :: Interactive m => InitFlags -> m PackageType -> m PackageType
getPackageType
InitFlags
{ initializeTestSuite = Flag True
, packageType = NoFlag
}
_ = return TestSuite
getPackageType flags act = fromFlagOrPrompt (packageType flags) act
getMainFile :: Interactive m => InitFlags -> m HsFilePath -> m HsFilePath
getMainFile flags act = case mainIs flags of
Flag a
| isHsFilePath a -> return $ toHsFilePath a
| otherwise -> act
NoFlag -> act
getInitializeTestSuite :: Interactive m => InitFlags -> m Bool -> m Bool
getInitializeTestSuite flags = fromFlagOrPrompt (initializeTestSuite flags)
getTestDirs :: Interactive m => InitFlags -> m [String] -> m [String]
getTestDirs flags = fromFlagOrPrompt (testDirs flags)
-- | Ask for the Haskell base language of the package.
getLanguage :: Interactive m => InitFlags -> m Language -> m Language
getLanguage flags = fromFlagOrPrompt (language flags)
-- | Ask whether to generate explanatory comments.
getNoComments :: Interactive m => InitFlags -> m Bool -> m Bool
getNoComments flags = fromFlagOrPrompt (noComments flags)
-- | Ask for the application root directory.
getAppDirs :: Interactive m => InitFlags -> m [String] -> m [String]
getAppDirs flags = fromFlagOrPrompt (applicationDirs flags)
-- | Ask for the source (library) root directory.
getSrcDirs :: Interactive m => InitFlags -> m [String] -> m [String]
getSrcDirs flags = fromFlagOrPrompt (sourceDirs flags)
-- | Retrieve the list of exposed modules
getExposedModules :: Interactive m => InitFlags -> m (NonEmpty ModuleName)
getExposedModules =
return
. fromMaybe (myLibModule NEL.:| [])
. join
. flagToMaybe
. fmap NEL.nonEmpty
. exposedModules
-- | Retrieve the list of other modules
getOtherModules :: Interactive m => InitFlags -> m [ModuleName]
getOtherModules = return . fromFlagOrDefault [] . otherModules
-- | Retrieve the list of build tools
getBuildTools :: Interactive m => InitFlags -> m [Dependency]
getBuildTools = flagElim (return []) (foldM go []) . buildTools
where
go acc dep = case eitherParsec dep of
Left e -> do
putStrLn $ "Failed to parse dependency: " ++ e
putStrLn "Skipping..."
return acc
Right d -> return $ acc ++ [d]
-- | Retrieve the list of dependencies
getDependencies
:: Interactive m
=> InitFlags
-> m [Dependency]
-> m [Dependency]
getDependencies flags = fromFlagOrPrompt (dependencies flags)
-- | Retrieve the list of extensions
getOtherExts :: Interactive m => InitFlags -> m [Extension]
getOtherExts = return . fromFlagOrDefault [] . otherExts
-- | Tell whether to overwrite files on write
getOverwrite :: Interactive m => InitFlags -> m Bool
getOverwrite = return . fromFlagOrDefault False . overwrite
-- -------------------------------------------------------------------- --
-- Shared prompts
simpleProjectPrompt :: Interactive m => InitFlags -> m Bool
simpleProjectPrompt flags =
getSimpleProject flags $
promptYesNo
"Should I generate a simple project with sensible defaults"
(DefaultPrompt True)
initializeTestSuitePrompt :: Interactive m => InitFlags -> m Bool
initializeTestSuitePrompt flags =
getInitializeTestSuite flags $
promptYesNo
"Should I generate a test suite for the library"
(DefaultPrompt True)
packageTypePrompt :: Interactive m => InitFlags -> m PackageType
packageTypePrompt flags = getPackageType flags $ do
pt <-
promptList
"What does the package build"
packageTypes
(DefaultPrompt "Executable")
Nothing
False
return $ fromMaybe Executable (parsePackageType pt)
where
packageTypes =
[ "Library"
, "Executable"
, "Library and Executable"
, "Test suite"
]
parsePackageType = \case
"Library" -> Just Library
"Executable" -> Just Executable
"Library and Executable" -> Just LibraryAndExecutable
"Test suite" -> Just TestSuite
_ -> Nothing
testMainPrompt :: Interactive m => m HsFilePath
testMainPrompt = do
fp <-
promptList
"What is the main module of the test suite?"
[defaultMainIs', "Main.lhs"]
(DefaultPrompt defaultMainIs')
Nothing
True
let hs = toHsFilePath fp
case _hsFileType hs of
InvalidHsPath -> do
putStrLn $
concat
[ "Main file "
, show hs
, " is not a valid haskell file. Source files must end in .hs or .lhs."
]
testMainPrompt
_ -> return hs
where
defaultMainIs' = show defaultMainIs
dependenciesPrompt
:: Interactive m
=> InstalledPackageIndex
-> InitFlags
-> m [Dependency]
dependenciesPrompt pkgIx flags = getDependencies flags (getBaseDep pkgIx flags)
-- -------------------------------------------------------------------- --
-- utilities
-- | If a flag is defined, return its value or else execute
-- an interactive action.
fromFlagOrPrompt
:: Interactive m
=> Flag a
-> m a
-> m a
fromFlagOrPrompt flag action = flagElim action return flag