{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE StrictData #-} {-# OPTIONS_HADDOCK prune #-} {- | Invoking a command-line program (be it tool or daemon) consists of listing the name of its binary, optionally supplying various options to adjust the behaviour of the program, and then supplying mandatory arguments, if any are specified. On startup, we parse any arguments passed in from the shell into @name,value@ pairs and incorporated into the resultant configuration stored in the program's Context. Additionally, this module allows you to specify environment variables that, if present, will be incorporated into the stored configuration. -} module Core.Program.Arguments ( -- * Setup Config , blankConfig , simpleConfig , simpleConfig' , complexConfig , complexConfig' , baselineOptions , Parameters (..) , ParameterValue (..) -- * Options and Arguments , LongName (..) , ShortName , Description , Options (..) -- * Programs with Commands , Commands (..) , appendOption -- * Internals , parseCommandLine , extractValidEnvironments , InvalidCommandLine (..) , buildUsage , buildVersion , emptyParameters ) where import Data.Hashable (Hashable) import Data.List qualified as List import Data.Maybe (fromMaybe) import Data.String (IsString (..)) import Prettyprinter ( Doc , Pretty (..) , align , emptyDoc , fillBreak , fillCat , fillSep , hardline , indent , nest , softline , (<+>) ) import Prettyprinter.Util (reflow) import System.Environment (getProgName) import Core.Data.Structures import Core.Program.Metadata import Core.System.Base import Core.Text.Rope import Core.Text.Utilities {- | Single letter "short" options (omitting the "@-@" prefix, obviously). -} type ShortName = Char {- | The description of an option, command, or environment variable (for use when rendering usage information in response to @--help@ on the command-line). -} type Description = Rope {- | The name of an option, command, or agument (omitting the "@--@" prefix in the case of options). This identifier will be used to generate usage text in response to @--help@ and by you later when retreiving the values of the supplied parameters after the program has initialized. Turn on __@OverloadedStrings@__ when specifying configurations, obviously. -} newtype LongName = LongName String deriving (Show, IsString, Eq, Hashable, Ord) instance Key LongName instance Pretty LongName where pretty (LongName name) = pretty name instance Textual LongName where intoRope (LongName str) = intoRope str fromRope = LongName . fromRope {- | The setup for parsing the command-line arguments of your program. You build a @Config@ with 'simpleConfig' or 'complexConfig', and pass it to 'Core.Program.Context.configure'. -} data Config = Blank | Simple Description [Options] | Complex Description [Commands] -- -- Those constructors are not exposed [and functions wrapping them are] partly -- for documentation convenience, partly for aesthetics (after a point too many -- constructors got a bit hard to differentiate betwen), and mostly so that if -- configure's argument turns into a monad like RequestBuilder we have -- somewhere to make that change. -- {- | A completely empty configuration, without the default debugging and logging options. Your program won't process any command-line options or arguments, which would be weird in most cases. Prefer 'simpleConfig'. @since 0.2.9 -} blankConfig :: Config blankConfig = Blank {- | Declare a simple (as in normal) configuration for a program with any number of optional parameters and mandatory arguments. For example: @ main :: 'IO' () main = do context <- 'Core.Program.Execute.configure' \"1.0\" 'Core.Program.Execute.None' ('simpleConfig' [ 'Option' "host" ('Just' \'h\') 'Empty' ['quote'| Specify an alternate host to connect to when performing the frobnication. The default is \"localhost\". |] , 'Option' "port" ('Just' \'p\') 'Empty' ['quote'| Specify an alternate port to connect to when frobnicating. |] , 'Option' "dry-run" 'Nothing' ('Value' \"TIME\") ['quote'| Perform a trial run at the specified time but don't actually do anything. |] , 'Option' "quiet" ('Just' \'q\') 'Empty' ['quote'| Supress normal output. |] , 'Argument' "filename" ['quote'| The file you want to frobnicate. |] ]) 'Core.Program.Execute.executeWith' context program @ which, if you build that into an executable called @snippet@ and invoke it with @--help@, would result in: @ \$ __./snippet --help__ Usage: snippet [OPTIONS] Available options: -h, --host Specify an alternate host to connect to when performing the frobnication. The default is \"localhost\". -p, --port Specify an alternate port to connect to when frobnicating. --dry-run=