opt-env-conf-0.12.1.0: src/OptEnvConf/Main.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module OptEnvConf.Main
( runSettingsParser,
runParser,
internalParser,
)
where
import qualified Data.Text as T
import Data.Version
import OptEnvConf.Args as Args
import OptEnvConf.Check
import OptEnvConf.Completion
import OptEnvConf.Doc
import qualified OptEnvConf.EnvMap as EnvMap
import OptEnvConf.Error
import OptEnvConf.Lint
import OptEnvConf.Nix
import OptEnvConf.Parser
import OptEnvConf.Reader
import OptEnvConf.Run
import OptEnvConf.Setting
import OptEnvConf.Terminal (getTerminalCapabilitiesFromHandle)
import Path
import System.Environment (getArgs, getEnvironment, getProgName)
import System.Exit
import System.IO
import Text.Colour
-- | Run 'runParser' on your @Settings@' type's 'settingsParser'.
--
-- __This is most likely the function you want to be using.__
runSettingsParser ::
(HasParser a) =>
-- | Program version, get this from Paths_your_package_name
Version ->
-- | Program description
String ->
IO a
runSettingsParser version progDesc =
runParser version progDesc settingsParser
-- | Run a parser
--
-- This function with exit on:
--
-- * Parse failure: show a nice error message.
-- * @-h|--help@: Show help text
-- * @--version@: Show version information
-- * @--render-man-page@: Render a man page
-- * @--bash-completion-script@: Render a bash completion script
-- * @--zsh-completion-script@: Render a zsh completion script
-- * @--fish-completion-script@: Render a fish completion script
-- * @query-opt-env-conf-completion@: Perform a completion query
--
-- This gets the arguments and environment variables from the current process.
runParser ::
-- | Program version, get this from Paths_your_package_name
Version ->
-- | Program description
String ->
Parser a ->
IO a
runParser version progDesc p = do
completeEnv <- getEnvironment
let envVars = EnvMap.parse completeEnv
case lintParser p of
Just errs -> do
tc <- getTerminalCapabilitiesFromHandle stderr
hPutChunksLocaleWith tc stderr $ renderLintErrors errs
exitFailure
Nothing -> do
let docs = parserDocs p
allArgs <- getArgs
let (debugMode, args) = consumeDebugMode allArgs
mDebugMode <-
if debugMode
then Just <$> getTerminalCapabilitiesFromHandle stderr
else pure Nothing
let (helpMode, args') = consumeHelpMode args
if helpMode
then do
progname <- getProgName
errOrDocs <- runHelpParser mDebugMode (Args.parseArgs args') p
case errOrDocs of
Left errs -> do
stderrTc <- getTerminalCapabilitiesFromHandle stderr
hPutChunksLocaleWith stderrTc stderr $ renderErrors errs
exitFailure
Right mCommandDoc -> do
tc <- getTerminalCapabilitiesFromHandle stdout
hPutChunksLocaleWith tc stdout $ case mCommandDoc of
Nothing -> renderHelpPage progname version progDesc docs
Just (path, cDoc) -> renderCommandHelpPage progname path cDoc
exitSuccess
else do
let (capabilities, args'') = consumeCapabilities args'
let (checkMode, args''') = consumeCheckMode args''
let readyArgs = Args.parseArgs args'''
let mConfig = Nothing -- We start with no config loaded.
if checkMode
then runSettingsCheck capabilities p readyArgs envVars mConfig
else do
let p' = internalParser p
errOrResult <-
runParserOn
capabilities
mDebugMode
p'
readyArgs
envVars
mConfig
case errOrResult of
Left errs -> do
tc <- getTerminalCapabilitiesFromHandle stderr
hPutChunksLocaleWith tc stderr $ renderErrors errs
exitFailure
Right i -> case i of
ShowVersion -> do
progname <- getProgName
tc <- getTerminalCapabilitiesFromHandle stdout
hPutChunksLocaleWith tc stdout $ renderVersionPage progname version
exitSuccess
RenderMan -> do
progname <- getProgName
tc <- getTerminalCapabilitiesFromHandle stdout
hPutChunksLocaleWith tc stdout $ renderManPage progname version progDesc docs
exitSuccess
RenderDocumentation -> do
progname <- getProgName
tc <- getTerminalCapabilitiesFromHandle stdout
hPutChunksLocaleWith tc stdout $ renderReferenceDocumentation progname docs
exitSuccess
RenderNixosOptions -> do
putStrLn $ T.unpack $ renderParserNixOptions p'
exitSuccess
BashCompletionScript progPath -> do
progname <- getProgName
generateBashCompletionScript progPath progname
exitSuccess
ZshCompletionScript progPath -> do
progname <- getProgName
generateZshCompletionScript progPath progname
exitSuccess
FishCompletionScript progPath -> do
progname <- getProgName
generateFishCompletionScript progPath progname
exitSuccess
CompletionQuery enriched index ws -> do
runCompletionQuery p' enriched index ws
exitSuccess
ParsedNormally a -> pure a
-- We use [String] instead of [Args] because we want to remove these args, and act on them, before any real arg parsing happens.
consumeExactArg :: String -> [String] -> (Bool, [String])
consumeExactArg arg = go
where
go = \case
[] -> (False, [])
(x : xs)
| x == arg -> (True, xs)
| otherwise ->
let (found, rest) = go xs
in (found, x : rest)
consumeDebugMode :: [String] -> (Bool, [String])
consumeDebugMode = consumeExactArg "--debug-optparse"
-- Note: Here we only consume exact -h as an arg, not -h as part of another arg like -hu
consumeHelpMode :: [String] -> (Bool, [String])
consumeHelpMode as =
let (found, as') = consumeExactArg "--help" as
in if found
then (True, as')
else consumeExactArg "-h" as'
consumeCheckMode :: [String] -> (Bool, [String])
consumeCheckMode = consumeExactArg "--run-settings-check"
consumeCapabilities :: [String] -> (Capabilities, [String])
consumeCapabilities = go allCapabilities
where
go :: Capabilities -> [String] -> (Capabilities, [String])
go caps = \case
[] -> (caps, [])
(x : xs) ->
let t = T.pack x
in case T.stripPrefix "--settings-capabilities-disable-" t of
Just capName -> go (disableCapability (Capability capName) caps) xs
Nothing -> case T.stripPrefix "--settings-capabilities-enable-" t of
Just capName -> go (enableCapability (Capability capName) caps) xs
Nothing ->
let (finalCaps, rest) = go caps xs
in (finalCaps, x : rest)
-- Internal structure to help us do what the framework
-- is supposed to.
data Internal a
= ShowVersion
| RenderMan
| RenderDocumentation
| RenderNixosOptions
| BashCompletionScript (Path Abs File)
| ZshCompletionScript (Path Abs File)
| FishCompletionScript (Path Abs File)
| CompletionQuery
-- Enriched
!Bool
-- Index
!Int
-- Args
![String]
| ParsedNormally !a
internalParser :: Parser a -> Parser (Internal a)
internalParser p =
choice
[ setting
[ switch ShowVersion,
long "version",
hidden
],
setting
[ switch RenderMan,
long "render-man-page",
hidden,
help "Render a manpage"
],
setting
[ switch RenderDocumentation,
long "render-reference-documentation",
hidden,
help "Render reference documentation"
],
setting
[ switch RenderNixosOptions,
long "render-nix-options",
hidden,
help "Render Nix options"
],
BashCompletionScript
<$> setting
[ option,
reader $ maybeReader parseAbsFile,
long "bash-completion-script",
hidden,
help "Render the bash completion script"
],
ZshCompletionScript
<$> setting
[ option,
reader $ maybeReader parseAbsFile,
long "zsh-completion-script",
hidden,
help "Render the zsh completion script"
],
ZshCompletionScript
<$> setting
[ option,
reader $ maybeReader parseAbsFile,
long "fish-completion-script",
hidden,
help "Render the fish completion script"
],
setting
[ help "Query completion",
switch CompletionQuery,
-- Long string that no normal user would ever use.
long "query-opt-env-conf-completion",
hidden
]
<*> setting
[ switch True,
long "completion-enriched",
value False,
hidden,
help "Whether to enable enriched completion"
]
<*> setting
[ option,
reader auto,
long "completion-index",
hidden,
help "The index between the arguments where completion was invoked."
]
<*> many
( setting
[ option,
reader str,
long "completion-word",
hidden,
help "The words (arguments) that have already been typed"
]
),
ParsedNormally <$> p
]