Fix #18; Fix parsing of sub-commands with descriptions

This commit is contained in:
Zahary Karadjov 2020-06-16 18:22:41 +03:00
parent 26667818be
commit aac25d1610
No known key found for this signature in database
GPG Key ID: C8936F8A3073D609
4 changed files with 88 additions and 21 deletions

View File

@ -102,7 +102,6 @@ when useBufferedOutput:
for arg in args:
help.add arg
template errorOutput(args: varargs[string]) =
helpOutput(args)
@ -123,6 +122,8 @@ const
fgSection = fgYellow
fgCommand = fgCyan
fgOption = fgBlue
fgArg = fgBlue
# TODO: Start using these:
# fgValue = fgGreen
# fgType = fgYellow
@ -147,6 +148,7 @@ func firstArgIdx(cmd: CmdInfo): int =
while result > 0:
if cmd.opts[result - 1].kind != Arg:
return
dec result
iterator args(cmd: CmdInfo): OptInfo =
if cmd.hasArgs:
@ -234,13 +236,21 @@ proc describeInvocation(help: var string,
helpOutput "\p"
if cmd.desc.len > 0:
helpOutput "\p ", cmd.desc, "\p"
helpOutput "\p", cmd.desc, "\p"
var argsSectionStarted = false
for arg in cmd.args:
if arg.desc.len > 0:
let cliArg = "<" & arg.humaneName & ">"
helpOutput cliArg, padding(cliArg, 6 + appInfo.namesWidth)
if not argsSectionStarted:
helpOutput "\p"
argsSectionStarted = true
let cliArg = " <" & arg.humaneName & ">"
helpOutput fgArg, styleBright, cliArg
helpOutput padding(cliArg, 6 + appInfo.namesWidth)
help.writeDesc appInfo, arg.desc
helpOutput "\p"
type
OptionsType = enum
@ -565,8 +575,8 @@ template debugMacroResult(macroName: string) {.dirty.} =
echo "\n-------- ", macroName, " ----------------------"
echo result.repr
macro generateFieldSetters(RecordType: type): untyped =
var recordDef = RecordType.getType[1].getImpl
proc generateFieldSetters(RecordType: NimNode): NimNode =
var recordDef = getImpl(RecordType)
let makeDefaultValue = bindSym"makeDefaultValue"
result = newTree(nnkStmtListExpr)
@ -622,10 +632,11 @@ macro generateFieldSetters(RecordType: type): untyped =
result.add settersArray
debugMacroResult "Field Setters"
macro buildCommandTree(RecordType: type): untyped =
proc cmdInfoFromType(T: NimNode): CmdInfo =
result = CmdInfo()
var
recordDef = RecordType.getType[1].getImpl
res = CmdInfo()
recordDef = getImpl(T)
discriminatorFields = newSeq[OptInfo]()
fieldIdx = 0
@ -682,16 +693,28 @@ macro buildCommandTree(RecordType: type): untyped =
if field.caseField != nil and field.caseBranch != nil:
let fieldName = field.caseField.getFieldName
var discriminator = findOpt(discriminatorFields, $fieldName)
if discriminator == nil:
error "Unable to find " & $fieldName
if field.caseBranch.kind == nnkElse:
error "Sub-command parameters cannot appear in an else branch. " &
"Please specify the sub-command branch precisely", field.caseBranch[0]
let branchEnumVal = field.caseBranch[0]
var cmd = findCmd(discriminator.subCmds, $branchEnumVal)
cmd.opts.add opt
else:
res.opts.add opt
result = newLitFixed(res)
debugMacroResult "Command Tree"
else:
result.opts.add opt
macro configurationRtti(RecordType: type): untyped =
let
T = RecordType.getType[1]
cmdInfo = cmdInfoFromType T
fieldSetters = generateFieldSetters T
result = newTree(nnkPar, newLitFixed cmdInfo, fieldSetters)
proc load*(Configuration: type,
cmdLine = commandLineParams(),
@ -711,10 +734,9 @@ proc load*(Configuration: type,
# This is an initial naive implementation that will be improved
# over time.
let fieldSetters = generateFieldSetters(Configuration)
let (rootCmd, fieldSetters) = configurationRtti(Configuration)
var fieldCounters: array[fieldSetters.len, int]
var rootCmd = buildCommandTree(Configuration)
printCmdTree rootCmd
let confAddr = addr result
@ -768,7 +790,8 @@ proc load*(Configuration: type,
template activateCmd(discriminator: OptInfo, activatedCmd: CmdInfo) =
let cmd = activatedCmd
applySetter(discriminator.idx, TaintedString(cmd.name))
applySetter(discriminator.idx, if cmd.desc.len > 0: TaintedString(cmd.desc)
else: TaintedString(cmd.name))
activeCmds.add cmd
nextArgIdx = cmd.getNextArgIdx(-1)

View File

@ -1,3 +1,6 @@
import
options
type
ConfigurationError* = object of CatchableError
@ -14,6 +17,20 @@ type
OutDir* = distinct string
OutFile* = distinct string
RestOfCmdLine* = distinct string
SubCommandArgs* = distinct string
Flag* = object
name*: TaintedString
FlagWithValue* = object
name*: TaintedString
value*: TaintedString
FlagWithOptionalValue* = object
name*: TaintedString
value*: Option[TaintedString]
Unspecified* = object
Txt* = object
@ -32,6 +49,7 @@ template defaultValue*(v: untyped) {.pragma.}
template required* {.pragma.}
template command* {.pragma.}
template argument* {.pragma.}
template inlineConfiguration* {.pragma.}
template implicitlySelectable* {.pragma.}
## This can be applied to a case object discriminator

View File

@ -0,0 +1,23 @@
import
confutils, options
type
Cmd = enum
fizz = "command A"
buzz = "command B"
Conf = object
case cmd {.command.}: Cmd
of fizz:
option {.desc: "some option".}: Option[string]
arg1 {.
argument
desc: "argument 1" .}: string
argument2 {.
argument
desc: "argument 2" .}: int
of buzz:
discard
echo load(Conf)

View File

@ -7,12 +7,12 @@ type
outerCmd2
InnerCmd = enum
innerCmd1
innerCmd1 = "Inner cmd 1"
innerCmd2
OuterOpt = enum
outerOpt1
outerOpt2
outerOpt1 = "Outer option 1"
outerOpt2 = "Outer option 2"
InnerOpt = enum
innerOpt1
@ -34,6 +34,9 @@ type
discard
of outerOpt2:
ooMandatory: string
ooOptiona {.
defaultValue: "test"
desc: "Outer option optional" .}: string
case cmd {.command.}: OuterCmd
of outerCmd1:
@ -43,8 +46,8 @@ type
ic1Optional {.
desc: "Delay in seconds"
abbr: "s" .}: Option[int]
else:
discard
of innerCmd2:
innerArg {.argument.}: string
of outerCmd2:
oc2Mandatory: int