This is an include file used by cligen.nim proper to initialize the clCfg global. Here we use only a parsecfg config file to do so.
Types
ClAlias = tuple[long: string, short: char, helpStr: string, dfl: seq[seq[string]]]
- User CL aliases
ClCfg = object version*: string hTabCols*: seq[ClHelpCol] ## selects columns to format hTabRowSep*: string ## separates rows, e.g. "\n" double spaces hTabColGap*: int ## number of spaces to separate cols by hTabMinLast*: int ## narrowest rightmost col no matter term width hTabVal4req*: string ## ``"REQUIRED"`` (or ``"NEEDED"``, etc.). reqSep*: bool ## ``parseopt3.initOptParser`` parameter sepChars*: set[char] ## ``parseopt3.initOptParser`` parameter opChars*: set[char] ## ``parseopt3.initOptParser`` parameter longPfxOk*: bool ## ``parseopt3.initOptParser`` parameter stopPfxOk*: bool ## ``parseopt3.initOptParser`` parameter subRowSep*: string ## separates full help dump subcmds; Eg.: "\n" hTabSuppress*: string ## Magic val for per-param help to suppress helpAttr*: Table[string, string] ## Text attrs for each help area helpAttrOff*: Table[string, string] ## Text attr offs for each help area noHelpHelp*: bool ## Elide --help, --help-syntax from help table useHdr*: string ## Override of const usage header template use*: string ## Override of const usage template useMulti*: string ## Override of const subcmd table template helpSyntax*: string ## Override of const syntaxHelp string render*: proc (s: string): string ## string->string help transformer widthEnv*: string ## name of environment var for width override sigPIPE*: ClSIGPIPE ## `dispatch` use allows end-user SIGPIPE ctrl minStrQuoting*: bool ## Only quote string defaults when necessary trueDefaultStr*: string ## How to render a default value of "true" falseDefaultStr*: string ## How to render a default value of "false" wrapDoc*: int ## Terminal column to wrap at for doc-like &.. wrapTable*: int ## ..Table-like sections. 0 => auto -1 => never
- Settings tending to be program- or CLI-author-global
ClHelpCol = enum clOptKeys, clValType, clDflVal, clDescrip
ClParse = tuple[paramName: string, ## Parse status for param ## Param name/long opt key unparsedVal: string, ## Unparsed val ("" for missing) message: string, ## default error message status: ClStatus]
- Parse status for param
ClSIGPIPE = enum spRaise = "raise", spPass = "pass", spIsOk = "isOk"
ClStatus = enum clBadKey, ## Unknown long key clBadVal, ## Unparsable value clNonOption, ## Unexpected non-option clMissing, ## Required but missing clParseOptErr, ## parseopt error clOk, ## Option parse part ok clPositional, ## Expected non-option clHelpOnly, clVersionOnly ## Early Exit requests
HelpError = object of CatchableError
- User-Syntax/Semantic Err; ${HELP}
HelpOnly = object of CatchableError
- Ok Ctl Flow Only For --help
ParseError = object of CatchableError
- CL-Syntax Err from generated code
VersionOnly = object of CatchableError
- Ok Ctl Flow Only For --version
Vars
clCfg = ClCfg(version: "", hTabCols: @[clOptKeys, clValType, clDflVal, clDescrip], hTabRowSep: "", hTabColGap: 2, hTabMinLast: 16, hTabVal4req: "REQUIRED", reqSep: false, sepChars: {'=', ':'}, opChars: { '+', '-', '*', '/', '%', '@', ',', '.', '&', '|', '~', '^', '$', '#', '<', '>', '?'}, longPfxOk: true, stopPfxOk: true, subRowSep: "", hTabSuppress: "CLIGEN-NOHELP", helpAttr: initTable(32), helpAttrOff: initTable(32), helpSyntax: syntaxHelp, render: descape, widthEnv: "CLIGEN_WIDTH", sigPIPE: spIsOk, minStrQuoting: false, trueDefaultStr: "true", falseDefaultStr: "false")
Lets
cgSetByParseNil = addr(setByParseDum)
cgVarSeqStrNil = addr(varSeqStrDum)
Consts
ClErrors = {clBadKey, clBadVal, clNonOption, clMissing}
ClExit = {clHelpOnly, clVersionOnly}
ClNoCall = {clBadKey..clMissing, clHelpOnly..clVersionOnly}
clUse = "$command $args\n${doc}Options:\n$options"
clUseHdr = "Usage:\n "
clUseMulti = "${doc}Usage:\n $command {SUBCMD} [sub-command options & parameters]\nwhere {SUBCMD} is one of:\n$subcmds\n$command {-h|--help} or with no args at all prints this message.\n$command --help-syntax gives general cligen syntax help.\nRun \"$command {help SUBCMD|SUBCMD --help}\" to see help for just SUBCMD.\nRun \"$command help\" to get *comprehensive* help.${ifVersion}"
clUseMultiGeneral = """$command {-h|--help} or with no args at all prints this message. $command --help-syntax gives general cligen syntax help. Run "$command {help SUBCMD|SUBCMD --help}" to see help for just SUBCMD. Run "$command help" to get *comprehensive* help.${ifVersion}"""
clUseMultiPerlish = "NAME\n ${doc}USAGE\n $command {SUBCMD} [sub-command options & parameters]\n\nSUBCOMMANDS\n$subcmds\n$command {-h|--help} or with no args at all prints this message.\n$command --help-syntax gives general cligen syntax help.\nRun \"$command {help SUBCMD|SUBCMD --help}\" to see help for just SUBCMD.\nRun \"$command help\" to get *comprehensive* help.${ifVersion}"
Procs
proc ha0(key: string; c = clCfg): string {....raises: [], tags: [], forbids: [].}
- Internal routine to access c.helpAttr
proc ha1(key: string; c = clCfg): string {....raises: [], tags: [], forbids: [].}
- Internal routine to access c.helpAttrOff
proc mergeParams(cmdNames: seq[string]; cmdLine = commandLineParams()): seq[ string] {....raises: [], tags: [], forbids: [].}
- This is a pass-through parameter merge to provide a hook for CLI authors to create the seq[string] to be parsed from any run-time sources (likely based on cmdNames) that they would like. In a single dispatch context, cmdNames[0] is the cmdName while in a dispatchMulti context it is @[ <mainCommand>, <subCommand> ].
proc numOfStatus(x: openArray[ClParse]; stati: set[ClStatus]): int {....raises: [], tags: [], forbids: [].}
- Count elements in the setByParse seq with parse status in stati.
proc quits(s: int) {....raises: [], tags: [], forbids: [].}
- quits)afe|s)aturating is for non-literal/maybe big input for compatibility w/older Nim stdlibs. Value is clipped to -128..127. Note that Unix shells often map a signaled exit to SIGNUM-128, e.g. 2-128 for SIGINT.
proc SIGPIPE_isOk() {....raises: [], tags: [], forbids: [].}
- Install signal handler to exit success upon OS posting SIGPIPE. This is more or less what (non-network) programs/users "expect".
proc SIGPIPE_pass() {....raises: [], tags: [], forbids: [].}
- Restore default signal disposition to allow OS to post SIGPIPE and likely terminate with non-zero exit status (typically 141=128+signo13). This optimizes for "no surprises" behavior of exec()d code reasonably expecting to inherit a near default SIGPIPE disposition.
proc topLevelHelp(doc: auto; use: auto; cmd: auto; subCmds: auto; subDocs: auto): string
Macros
macro cligenQuitAux(cmdLine: seq[string]; dispatchName: string; cmdName: string; pro: untyped; echoResult: bool; noAutoEcho: bool; mergeNames: seq[string] = @[]): untyped
macro dispatchGen(pro: typed{nkSym}; cmdName: string = ""; doc: string = ""; help: typed = {}; short: typed = {}; usage: string = clUse; cf: ClCfg = clCfg; echoResult = false; noAutoEcho = false; positional: static string = AUTO; suppress: seq[string] = @[]; implicitDefault: seq[string] = @[]; vars: seq[string] = @[]; dispatchName = ""; mergeNames: seq[string] = @[]; alias: seq[ClAlias] = @[]; stopWords: seq[string] = @[]; noHdr = false; docs: ptr var seq[string] = cgVarSeqStrNil; setByParse: ptr var seq[ClParse] = cgSetByParseNil): untyped
-
Generate command-line dispatcher for proc pro named dispatchName (defaulting to dispatchPro) with generated help/syntax guided by cmdName, doc, and cf. Parameters with no explicit default in the proc become required command arguments while those with default values become command options. Each proc parameter type needs in-scope argParse & argHelp procs. cligen/argcvt defines them for basic types & basic collections (int, string, enum, .., seq[T], set[T], ..).
help is a {(paramNm, str)} of per-param help, eg. {"quiet": "be quiet"}. Often, only these help strings are needed for a decent CLI. A row of the help table can be suppressed from showing by setting str to a magic clCfg.hTabSuppress value (defaults to "CLIGEN-NOHELP", but is customizable).
short is a {(paramNm, char)} of per-param single-char option keys. Setting a parameter value to '\0' suppresses the assignment of a short option. Suppress all short options by passing an empty key: { "": ' ' }.
help & short definitions outside a call require explicit toTable (from std/tables) conversions.
usage is a help template interpolating $command $args $doc $options.
cf controls whole program aspects of generated CLIs. See ClCfg docs.
Default exit protocol is: quits(int(result)) or (echo $result or discard; quit(0)) depending on what compiles. True echoResult forces echo while noAutoEcho blocks it (=>int()||discard). Technically, cligenQuit implements this behavior.
By default, cligen maps the first non-defaulted seq[] proc parameter to any non-option/positional command args. positional selects another. Set positional to the empty string ("") to disable this entirely.
suppress is a list of formal parameter names to exclude from the parse- assign system. Such names are effectively pinned to their default values.
implicitDefault is a list of formal parameter names allowed to default to the Nim default value for a type, rather than becoming required, when they lack an explicit initializer.
vars is a seq[string] of outer scope-declared variables bound to the CLI (e.g., on CL, --logLvl=X converts & assigns to outer.logLvl = X).
stopWords is a seq[string] of words beyond which -.* no longer signifies an option (like the common sole -- command argument).
noHdr is a (mostly internal) flag to suppress emitting "Usage:" header in generated help tables (e.g. in a multi-command).
mergeNames gives the cmdNames param passed to mergeParams, which defaults to @[cmdName] if mergeNames is @[].
alias is @[] | 2-seq of (string,char,string) 3-tuples specifying (Long, Short opt keys, Help) to {Define,Reference} aliases. This lets CL users define aliases early in mergeParams sources (e.g. cfg files/evars) & reference them later. Eg. if alias[0][0]=="alias" and alias[1][1]=='a' then --alias:k='-a -b' -ak expands to @["-a", "-b"].
docs is addr(some var seq[string]) to which to append each main doc comment or its replacement doc=text. Default of nil means do nothing.
setByParse is addr(some var seq[ClParse]). When non-nil, this var collects each parameter seen, keyed under its long/param name (i.e., parsed but not converted to native types). Wrapped procs can inspect this or even convert args themselves to revive parseopt-like iterative interpreting. cligen provides convenience procs to use setByParse: contains, numOfStatus & next. Note that ordinary Nim procs, from inside calls, do not know how params got their values (positional, keyword, defaulting). Wrapped procs accessing setByParse are inherently command-line only. So, this var seq needing to be declared before such procs for such access is ok. Ideally, keep important functionality Nim-callable. setByParse may also be useful combined with the parseOnly arg of generated dispatchers.
macro dispatchMulti(procBrackets: varargs[untyped]): untyped
-
A wrapper to generate a multi-command dispatcher, call it, and quit. The argument is a list of bracket expressions passed to dispatchGen for each subcommand.
The VERY FIRST bracket can be the special string literal "multi" to adjust dispatchGen settings for the top-level proc that dispatches to subcommands. In particular, top-level usage is a string template interpolating $command $doc $subcmds $ifVersion (args & options dropped and subcmd & ifVersion added relative to an ordinary dispatchGen usage template.).
macro dispatchMultiDG(procBkts: varargs[untyped]): untyped
- An internal macro to generate the call to dispatchGen(itsMulti).
macro dispatchMultiGen(procBkts: varargs[untyped]): untyped
- Generate multi-cmd dispatch. procBkts are argLists for dispatchGen. Eg., dispatchMultiGen([foo, short={"dryRun": "n"}], [bar, doc="Um"]).
macro initDispatchGen(dispName, obName: untyped; default: typed; positional = ""; suppress: seq[string] = @[]; body: untyped): untyped
-
Create a proc with signature from default that calls initGen and initializes var obName by calling to the generated initializer. It puts body inside an appropriate try/except so that you can just say:
initDispatchGen(cmdProc, cfg, cfgDfl): cfg.callAPI() quit(min(127, cfg.nError)) dispatch(cmdProc)
macro initGen(default: typed; T: untyped; positional = ""; suppress: seq[string] = @[]; name = ""): untyped
- This macro generates an init proc for object|tuples of type T with param names equal to top-level field names & default values from default like init(field1=default.field1,...): T = result.field1=field1; .., except if fieldN==positional fieldN: typeN is used instead which in turn makes dispatchGen bind that seq to catch positional CL args.
Templates
template ambigSubcommand(cb: CritBitTree[string]; attempt: string)
template cligenHelp(p: untyped; hlp: untyped; use: untyped; pfx: untyped; skipHlp: untyped; noUHdr = false): auto
template cligenQuit(p: untyped; echoResult = false; noAutoEcho = false): auto
template dispatch(pro: typed{nkSym}; cmdName = ""; doc = ""; help: typed = {}; short: typed = {}; usage = clUse; echoResult = false; noAutoEcho = false; positional = AUTO; suppress: seq[string] = @[]; implicitDefault: seq[string] = @[]; vars: seq[string] = @[]; dispatchName = ""; mergeNames: seq[string] = @[]; alias: seq[ClAlias] = @[]; stopWords: seq[string] = @[]; noHdr = false): untyped
- Convenience dispatchCf wrapper to silence bogus GcUnsafe warnings at verbosity:2. Parameters are the same as dispatchCf (except for no cf).
template dispatchCf(pro: typed{nkSym}; cmdName = ""; doc = ""; help: typed = {}; short: typed = {}; usage = clUse; cf: ClCfg = clCfg; echoResult = false; noAutoEcho = false; positional = AUTO; suppress: seq[string] = @[]; implicitDefault: seq[string] = @[]; vars: seq[string] = @[]; dispatchName = ""; mergeNames: seq[string] = @[]; alias: seq[ClAlias] = @[]; stopWords: seq[string] = @[]; noHdr = false; cmdLine = commandLineParams()): untyped
- A convenience wrapper to both generate a command-line dispatcher and then call the dispatcher & exit; Params are same as the dispatchGen macro.
template initFromCL[T](default: T; cmdName: string = ""; doc: string = ""; help: typed = {}; short: typed = {}; usage: string = clUse; positional = ""; suppress: seq[string] = @[]; mergeNames: seq[string] = @[]; alias: seq[ClAlias] = @[]): T
- Convenience initFromCLcf wrapper to silence bogus GcUnsafe warnings at verbosity:2. Parameters are as for initFromCLcf (except for no cf).
template initFromCLcf[T](default: T; cmdName: string = ""; doc: string = ""; help: typed = {}; short: typed = {}; usage: string = clUse; cf: ClCfg = clCfg; positional = ""; suppress: seq[string] = @[]; mergeNames: seq[string] = @[]; alias: seq[ClAlias] = @[]): T
- Like dispatchCf but only quit when user gave bad CL, --help, or --version. On success, returns T populated from object|tuple default and then from the mergeNames/the command-line. Top-level fields must have types with argParse & argHelp overloads. Params to this common to dispatchGen mean the same thing. The inability here to distinguish between syntactically explicit assigns & Nim type defaults eliminates several features and makes the positional default be empty.
template unknownSubcommand(cmd: string; subCmds: seq[string])