mirror of
https://github.com/daylinmorgan/hwylterm.git
synced 2025-02-22 09:25:49 -06:00
positional arg parsing revamp
This commit is contained in:
parent
938f9d411d
commit
8f98b53b91
11 changed files with 273 additions and 46 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,6 +1,3 @@
|
|||
tests/*
|
||||
!tests/*.nim
|
||||
!tests/*.nims
|
||||
nimble.develop
|
||||
nimble.paths
|
||||
nimbledeps
|
||||
|
|
|
@ -3,6 +3,7 @@ import std/[os, strformat, strutils]
|
|||
task test, "run tests":
|
||||
selfExec "r tests/tbbansi.nim"
|
||||
selfExec "r tests/tcli.nim"
|
||||
selfExec "r tests/cli/tcli.nim"
|
||||
|
||||
task develop, "install cligen for development":
|
||||
exec "nimble install -l 'cligen@1.7.5'"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
]##
|
||||
|
||||
import std/[
|
||||
algorithm,
|
||||
macros, os, sequtils,
|
||||
sets, strutils, tables,
|
||||
sugar
|
||||
|
@ -149,7 +150,9 @@ type
|
|||
GenerateOnly, ## Don't attach root `runProc()` node
|
||||
NoHelpFlag, ## Remove the builtin help flag
|
||||
ShowHelp, ## If cmdline empty show help
|
||||
NoNormalize ## Don't normalize flags and commands
|
||||
NoNormalize, ## Don't normalize flags and commands
|
||||
NoPositional ## Raise error if any remaing positional arguments
|
||||
ExactArgs, ## Raise error if missing positional argument
|
||||
|
||||
BuiltinFlag = object
|
||||
name*: string
|
||||
|
@ -179,6 +182,9 @@ type
|
|||
header*, footer*, description*, usage*, styles*: NimNode
|
||||
|
||||
CliArg = object
|
||||
name: string
|
||||
ident: NimNode
|
||||
typeNode: NimNode
|
||||
|
||||
CliCfg = object
|
||||
name*: string
|
||||
|
@ -200,6 +206,11 @@ type
|
|||
inherit*: Inherit
|
||||
root*: bool
|
||||
|
||||
|
||||
func err(c: CliCfg, msg: string) =
|
||||
## quit with error while generating cli
|
||||
error "\nfailed to generate '" & c.name & "' hwylcli: \n" & msg
|
||||
|
||||
template `<<<`(s: string) {.used.} =
|
||||
let pos = instantiationInfo()
|
||||
debugEcho "$1:$2" % [pos.filename, $pos.line]
|
||||
|
@ -211,7 +222,6 @@ template `<<<`(n: NimNode) {.used.} =
|
|||
## for debugging macros
|
||||
<<< treeRepr n
|
||||
|
||||
|
||||
func `<<<`(f: CliFlag) {.used.}=
|
||||
var s: string
|
||||
let fields = [
|
||||
|
@ -246,6 +256,7 @@ func getFlagParamNode(node: NimNode): NimNode =
|
|||
result = node[1]
|
||||
else: bad(node, "flag param")
|
||||
|
||||
# TODO: also accept the form `flag: "help"`
|
||||
func parseFlagParams(f: var CliFlag, node: NimNode) =
|
||||
expectKind node, nnkStmtList
|
||||
for n in node:
|
||||
|
@ -307,6 +318,12 @@ func parseCliFlag(n: NimNode): CliFlag =
|
|||
result.typeNode = ident"bool"
|
||||
|
||||
func postParse(cfg: var CliCfg) =
|
||||
if cfg.name == "":
|
||||
error "missing required option: name"
|
||||
|
||||
if cfg.args.len != 0 and cfg.subcommands.len != 0:
|
||||
error "args and subcommands are mutually exclusive"
|
||||
|
||||
let defaultTypeNode = cfg.defaultFlagType or ident"bool"
|
||||
for f in cfg.flagDefs.mitems:
|
||||
if f.typeNode == nil:
|
||||
|
@ -314,6 +331,11 @@ func postParse(cfg: var CliCfg) =
|
|||
if f.group in ["", "global"]:
|
||||
cfg.flags.add f
|
||||
|
||||
if cfg.args.len > 0:
|
||||
let count = cfg.args.filterIt(it.typeNode.kind == nnkBracketExpr).len
|
||||
if count > 1:
|
||||
cfg.err "more than one positional argument is variadic"
|
||||
|
||||
func parseCliFlags(cfg: var CliCfg, node: NimNode) =
|
||||
var group: string
|
||||
expectKind node, nnkStmtList
|
||||
|
@ -514,11 +536,7 @@ func pasrseCliAlias(cfg: var CliCfg, node: NimNode) =
|
|||
cfg.alias.incl s
|
||||
else: bad(n, "alias")
|
||||
|
||||
func err(c: CliCfg, msg: string) =
|
||||
## quit with error while generating cli
|
||||
error "failed to generate " & c.name & " hwylcli: \n" & msg
|
||||
|
||||
func check(c: CliCfg) =
|
||||
func postPropagateCheck(c: CliCfg) =
|
||||
## verify the cli is valid
|
||||
var
|
||||
short: Table[char, CliFlag]
|
||||
|
@ -553,7 +571,7 @@ func propagate(c: var CliCfg) =
|
|||
child.post = c.postSub
|
||||
child.inheritFrom(c)
|
||||
propagate child
|
||||
check child
|
||||
postPropagateCheck child
|
||||
|
||||
|
||||
func parseCliHelp(c: var CliCfg, node: NimNode) =
|
||||
|
@ -588,7 +606,6 @@ func parseCliHelp(c: var CliCfg, node: NimNode) =
|
|||
of nnkCall:
|
||||
if node[1].kind != nnkStmtList:
|
||||
error "expected list of arguments for help"
|
||||
|
||||
for n in node[1]:
|
||||
expectLen n, 2
|
||||
let id = n[0].strVal
|
||||
|
@ -599,7 +616,6 @@ func parseCliHelp(c: var CliCfg, node: NimNode) =
|
|||
of nnKCall:
|
||||
val = n[1][0]
|
||||
else: bad(n, id)
|
||||
|
||||
case id:
|
||||
of "usage": help.usage = val
|
||||
of "description": help.description = val
|
||||
|
@ -607,11 +623,46 @@ func parseCliHelp(c: var CliCfg, node: NimNode) =
|
|||
of "footer": help.footer = val
|
||||
of "styles": help.styles = val
|
||||
else: error "unknown help option: " & id
|
||||
|
||||
else: bad(node, "help")
|
||||
|
||||
c.help = help
|
||||
|
||||
func badNode(c: CliCfg, node: NimNode, msg: string) =
|
||||
c.err "unexpected node kind: " & $node.kind & "\n" & msg
|
||||
|
||||
func parseCliArg(c: CliCfg, node: NimNode): CliArg =
|
||||
expectLen node, 2
|
||||
result.name = node[0].strVal
|
||||
case node[1].kind
|
||||
of nnkStmtList:
|
||||
for n in node[1]:
|
||||
let id = n[0].strVal
|
||||
var val: NimNode
|
||||
case n.kind:
|
||||
of nnkCommand:
|
||||
val = n[1]
|
||||
of nnkCall:
|
||||
# input seq[string]
|
||||
if n[1].len == 2:
|
||||
result.typeNode = n[1][1]
|
||||
val = n[1][0]
|
||||
else: bad(n, id)
|
||||
case id:
|
||||
of "T": result.typeNode = val
|
||||
of "ident": result.ident = val
|
||||
else: c.err("unknown cli param: " & id & "provided for arg: " & result.name)
|
||||
of nnkIdent, nnkBracketExpr:
|
||||
result.typeNode = node[1]
|
||||
else:
|
||||
c.badNode(node[1], "parsing cli arg: " & result.name)
|
||||
if result.ident == nil:
|
||||
result.ident = ident(result.name)
|
||||
|
||||
func parseCliArgs(c: var CliCfg, node: NimNode) =
|
||||
if node.kind != nnkStmtList:
|
||||
bad(node, "expected node kind nnkStmtList")
|
||||
for n in node:
|
||||
c.args.add parseCliArg(c, n)
|
||||
|
||||
func parseCliBody(body: NimNode, name = "", root = false): CliCfg =
|
||||
result.name = name
|
||||
result.root = root
|
||||
|
@ -652,13 +703,13 @@ func parseCliBody(body: NimNode, name = "", root = false): CliCfg =
|
|||
result.postSub = node[1]
|
||||
of "defaultFlagType":
|
||||
result.defaultFlagType = node[1]
|
||||
of "args":
|
||||
parseCliArgs result, node[1]
|
||||
else:
|
||||
error "unknown hwylCli setting: " & name
|
||||
|
||||
if result.name == "":
|
||||
error "missing required option: name"
|
||||
|
||||
postParse result
|
||||
|
||||
# TODO: validate "required" flags exist here?
|
||||
result.addBuiltinFlags()
|
||||
|
||||
|
@ -700,6 +751,7 @@ proc hwylCliError*(msg: string) =
|
|||
quit $(bb("error ", "red") & bb(msg))
|
||||
|
||||
func defaultUsage(cfg: CliCfg): NimNode =
|
||||
# TODO: attempt to handle pos args
|
||||
var s = "[b]" & cfg.name & "[/]"
|
||||
if cfg.subcommands.len > 0:
|
||||
s.add " [bold italic]subcmd[/]"
|
||||
|
@ -887,8 +939,8 @@ func getNoVals(cfg: CliCfg): tuple[long: NimNode, short: NimNode] =
|
|||
)
|
||||
result = (nnkPrefix.newTree(ident"@",long), short)
|
||||
|
||||
func setFlagVars(cfg: CliCfg): NimNode =
|
||||
## generate all variables not covered in global module
|
||||
func setVars(cfg: CliCfg): NimNode =
|
||||
## generate all positinal variables and flags not covered in global module
|
||||
result = nnkVarSection.newTree()
|
||||
let flags =
|
||||
if cfg.root: cfg.flags
|
||||
|
@ -897,6 +949,10 @@ func setFlagVars(cfg: CliCfg): NimNode =
|
|||
result.add flags.mapIt(
|
||||
nnkIdentDefs.newTree(it.ident, it.typeNode, newEmptyNode())
|
||||
)
|
||||
if cfg.args.len > 0:
|
||||
result.add cfg.args.mapIt(
|
||||
nnkIdentDefs.newTree(it.ident, it.typeNode, newEmptyNode())
|
||||
)
|
||||
|
||||
func literalFlags(f: CliFlag): NimNode =
|
||||
var flags: seq[string]
|
||||
|
@ -904,7 +960,116 @@ func literalFlags(f: CliFlag): NimNode =
|
|||
if f.long != "": flags.add "[b]" & "--" & f.long & "[/]"
|
||||
result = newLit(flags.join("|"))
|
||||
|
||||
func addPostParseCheck(cfg: CliCfg, body: NimNode) =
|
||||
type
|
||||
MultiArgKind = enum
|
||||
NoMulti, ## No positionals use seq[[T]]
|
||||
First, ## First positional uses seq[[T]]
|
||||
Last, ## Last positional uses seq[[T]]
|
||||
|
||||
func getMultiArgKind(cfg: CliCfg): MultiArgKind =
|
||||
if cfg.args.len == 1:
|
||||
return First
|
||||
if cfg.args[0].typeNode.kind == nnkBracketExpr:
|
||||
return First
|
||||
if cfg.args[^1].typeNode.kind == nnkBracketExpr:
|
||||
return Last
|
||||
|
||||
func parseArgs(p: OptParser, target: var string) =
|
||||
target = p.key
|
||||
|
||||
func parseArgs[T](p: OptParser, target: var seq[T]) =
|
||||
var val: T
|
||||
parseArgs(p, val)
|
||||
target.add val
|
||||
|
||||
proc parseArgs*(arg: string, target: var float) =
|
||||
try: target = parseFloat(arg)
|
||||
except: hwylCliError("failed to parse as float: [b]" & arg)
|
||||
|
||||
func parseArgs*(arg: string, target: var string) =
|
||||
target = arg
|
||||
|
||||
proc parseArgs*(arg: string, target: var int) =
|
||||
try: target = parseInt(arg)
|
||||
except: hwylCliError("failed to parse as integer: [b]" & arg)
|
||||
|
||||
proc parseArgs*[E: enum](arg: string, target: var E) =
|
||||
try: target = parseEnum[E](arg)
|
||||
except:
|
||||
let choices = enumNames(E).join(",")
|
||||
hwylCliError("failed to parse as enum: [b]" & arg & "[/], expected one of: " & choices)
|
||||
|
||||
proc parseArgs*[T](arg: string, target: var seq[T]) =
|
||||
var val: T
|
||||
parseArgs(arg, val)
|
||||
target.add val
|
||||
|
||||
proc parseArgs*[T](args: seq[string], target: var seq[T]) =
|
||||
for arg in args:
|
||||
parseArgs(arg, target)
|
||||
|
||||
|
||||
# TODO: rework conditionals and control flow here...
|
||||
func genPosArgHandler(cfg: CliCfg, body: NimNode) =
|
||||
## generate code to handle positional arguments
|
||||
let numArgs = cfg.args.len
|
||||
let maKind = cfg.getMultiArgKind()
|
||||
if ExactArgs in cfg.settings:
|
||||
case maKind:
|
||||
of NoMulti:
|
||||
body.add quote do:
|
||||
if result.len != `numArgs`:
|
||||
hwylCliError("missing positional args, got: " & $result.len & ", expected: " & $`numArgs`)
|
||||
else:
|
||||
body.add quote do:
|
||||
if result.len < `numArgs`:
|
||||
hwylCliError("missing positional args, got: " & $result.len & ", expected: " & $`numArgs`)
|
||||
elif maKind == First:
|
||||
body.add quote do:
|
||||
if result.len < `numArgs`:
|
||||
hwylCliError("missing positional args, got: " & $result.len & ", expected at least: " & $`numArgs`)
|
||||
elif maKind == Last:
|
||||
body.add quote do:
|
||||
if result.len < (`numArgs` - 1):
|
||||
hwylCliError("missing positional args, got: " & $result.len & ", expected at least: " & $(`numArgs` - 1))
|
||||
|
||||
case maKind:
|
||||
# BUG: this may create index defects,
|
||||
# if not coupled with ExactArgs or result length checks
|
||||
of Last:
|
||||
for i, namedArg in cfg.args[0..^2].mapIt(it.ident):
|
||||
body.add quote do:
|
||||
parseArgs(result[`i`], `namedArg`)
|
||||
|
||||
let lastArg = cfg.args[^1].ident
|
||||
body.add quote do:
|
||||
parseArgs(result[(`numArgs`-1).. ^1],`lastArg`)
|
||||
|
||||
of First:
|
||||
for i, namedArg in cfg.args[1..^1].reversed().mapIt(it.ident):
|
||||
body.add quote do:
|
||||
parseArgs(result[^(1+`i`)], `namedArg`)
|
||||
|
||||
let firstArg = cfg.args[0].ident
|
||||
body.add quote do:
|
||||
parseArgs(result[0..^(`numArgs`)], `firstArg`)
|
||||
|
||||
|
||||
of NoMulti:
|
||||
for i, namedArg in cfg.args.mapIt(it.name.ident):
|
||||
body.add quote do:
|
||||
parseArgs(result[`i`], `namedArg`)
|
||||
|
||||
# clear out 'args'
|
||||
if ExactArgs in cfg.settings:
|
||||
if maKind == NoMulti:
|
||||
body.add quote do:
|
||||
result = @[(`numArgs`)..^1]
|
||||
else:
|
||||
body.add quote do:
|
||||
result = @[`numArgs`..^1]
|
||||
|
||||
func addPostParseHook(cfg: CliCfg, body: NimNode) =
|
||||
## generate block to set defaults and check for required flags
|
||||
let flagSet = ident"flagSet"
|
||||
var required, default: seq[CliFlag]
|
||||
|
@ -931,6 +1096,10 @@ func addPostParseCheck(cfg: CliCfg, body: NimNode) =
|
|||
if `name` notin `flagSet`:
|
||||
`target` = `default`
|
||||
|
||||
if cfg.args.len > 0:
|
||||
genPosArgHandler cfg, body
|
||||
|
||||
|
||||
func hwylCliImpl(cfg: CliCfg): NimNode
|
||||
|
||||
func genSubcommandHandler(cfg: CliCfg): NimNode =
|
||||
|
@ -961,16 +1130,11 @@ func genSubcommandHandler(cfg: CliCfg): NimNode =
|
|||
|
||||
result.add subCommandCase
|
||||
|
||||
func parseArgs(p: OptParser, target: var string) =
|
||||
target = p.key
|
||||
|
||||
func parseArgs[T](p: OptParser, target: var seq[T]) =
|
||||
var val: T
|
||||
parseArgs(p, val)
|
||||
target.add val
|
||||
|
||||
func argOfBranch(cfg: CliCfg): NimNode =
|
||||
# TODO: collect all strings into a seq and handle prior to subcomamnd parsing?
|
||||
# subcommands are really just a special case of positional args handling
|
||||
func positionalArgsOfBranch(cfg: CliCfg): NimNode =
|
||||
result = nnkOfBranch.newTree(ident"cmdArgument")
|
||||
# TODO: utilize the NoPositional setting here?
|
||||
# if cfg.args.len == 0 and cfg.subcommands.len == 0:
|
||||
# result.add quote do:
|
||||
# hwylCliError("unexpected positional argument: [b]" & p.key)
|
||||
|
@ -979,7 +1143,6 @@ func argOfBranch(cfg: CliCfg): NimNode =
|
|||
inc nArgs
|
||||
parseArgs(p, result)
|
||||
|
||||
|
||||
func hwylCliImpl(cfg: CliCfg): NimNode =
|
||||
let
|
||||
version = cfg.version or newLit("")
|
||||
|
@ -993,9 +1156,7 @@ func hwylCliImpl(cfg: CliCfg): NimNode =
|
|||
nArgs = ident"nargs"
|
||||
(longNoVal, shortNoVal) = cfg.getNoVals()
|
||||
printHelpProc = generateCliHelpProc(cfg, printHelpName)
|
||||
flagVars = setFlagVars(cfg)
|
||||
|
||||
result = newTree(nnkStmtList)
|
||||
flagVars = setVars(cfg)
|
||||
|
||||
var
|
||||
parserBody = nnkStmtList.newTree()
|
||||
|
@ -1035,8 +1196,8 @@ func hwylCliImpl(cfg: CliCfg): NimNode =
|
|||
nnkCaseStmt.newTree(
|
||||
ident"kind",
|
||||
nnkOfBranch.newTree(ident("cmdError"), quote do: hwylCliError(p.message)),
|
||||
nnkOfBranch.newTree(ident("cmdEnd"), quote do: assert false),
|
||||
argOfBranch(cfg),
|
||||
nnkOfBranch.newTree(ident("cmdEnd"), quote do: hwylCliError("reached cmdEnd unexpectedly.")),
|
||||
positionalArgsOfBranch(cfg),
|
||||
nnkOfBranch.newTree(
|
||||
ident("cmdShortOption"), ident("cmdLongOption"),
|
||||
shortLongCaseStmt(cfg, printHelpName, version)
|
||||
|
@ -1050,9 +1211,11 @@ func hwylCliImpl(cfg: CliCfg): NimNode =
|
|||
if commandLineParams().len == 0:
|
||||
`printHelpName`(); quit 1
|
||||
|
||||
addPostParseHook(cfg, parserBody)
|
||||
|
||||
let runProcName = ident("run" & name)
|
||||
let runBody = nnkStmtList.newTree()
|
||||
addPostParseCheck(cfg, parserBody)
|
||||
|
||||
# move to proc?
|
||||
if cfg.pre != nil:
|
||||
runBody.add cfg.pre
|
||||
|
@ -1061,9 +1224,12 @@ func hwylCliImpl(cfg: CliCfg): NimNode =
|
|||
if cfg.post != nil:
|
||||
runBody.add cfg.post
|
||||
|
||||
# args and subcommands need to be mutually exclusive -> implement using a CommandKind?
|
||||
if cfg.subcommands.len > 0:
|
||||
runBody.add genSubcommandHandler(cfg)
|
||||
|
||||
result = newTree(nnkStmtList)
|
||||
|
||||
result.add quote do:
|
||||
# block:
|
||||
`printHelpProc`
|
||||
|
@ -1083,7 +1249,6 @@ func hwylCliImpl(cfg: CliCfg): NimNode =
|
|||
result.add quote do:
|
||||
`runProcName`(`args`[1..^1])
|
||||
|
||||
|
||||
macro hwylCli*(body: untyped) =
|
||||
## generate a CLI styled by `hwylterm` and parsed by `parseopt3`
|
||||
var cfg = parseCliBody(body, root = true)
|
||||
|
|
5
test.nim
Normal file
5
test.nim
Normal file
|
@ -0,0 +1,5 @@
|
|||
import std/macros
|
||||
|
||||
dumpAstGen:
|
||||
block:
|
||||
echo "hello world"
|
4
tests/.gitignore
vendored
Normal file
4
tests/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*
|
||||
!*.nim
|
||||
!*.nims
|
||||
!cli/
|
4
tests/cli/.gitignore
vendored
Normal file
4
tests/cli/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*
|
||||
!refs/*
|
||||
!*.nim
|
||||
!*.nims
|
1
tests/cli/config.nims
Normal file
1
tests/cli/config.nims
Normal file
|
@ -0,0 +1 @@
|
|||
switch("path", "$projectDir/../../src")
|
29
tests/cli/lib.nim
Normal file
29
tests/cli/lib.nim
Normal file
|
@ -0,0 +1,29 @@
|
|||
import std/[compilesettings, os, osproc, strutils, times, unittest]
|
||||
|
||||
const pathToSrc = querySetting(SingleValueSetting.projectPath)
|
||||
const binDir = pathToSrc / "bin"
|
||||
const hwylCliSrc = pathToSrc / "../../src/hwylterm/hwylcli.nim"
|
||||
let hwylCliWriteTime = getFileInfo(hwylCliSrc).lastWriteTime
|
||||
|
||||
if not dirExists(binDir):
|
||||
createDir(binDir)
|
||||
|
||||
proc runTestCli(module: string, args: string, code: int = 0): string =
|
||||
let cmd = binDir / module & " " & args
|
||||
let (output, exitCode) = execCmdEx(cmd)
|
||||
check code == exitCode
|
||||
result = output.strip()
|
||||
|
||||
proc preCompileWorkingModule(module: string) =
|
||||
let exe = binDir / module
|
||||
let srcModule = pathToSrc / "clis" / (module & ".nim")
|
||||
if not exe.fileExists or getFileInfo(exe).lastWriteTime < max(getFileInfo(srcModule).lastWriteTime, hwylCliWriteTime):
|
||||
let cmd = "nim c -o:$1 $2" % [exe, srcModule]
|
||||
let code = execCmd(cmd)
|
||||
if code != 0:
|
||||
echo "cmd: ", cmd
|
||||
quit "failed to precompile test module"
|
||||
|
||||
proc checkRunWithArgs*(module: string, args = "", output = "", code = 0) =
|
||||
preCompileWorkingModule(module)
|
||||
check output == runTestCli(module, args, code)
|
11
tests/cli/tcli.nim
Normal file
11
tests/cli/tcli.nim
Normal file
|
@ -0,0 +1,11 @@
|
|||
import std/[unittest]
|
||||
import ./lib
|
||||
|
||||
suite "hwylcli":
|
||||
test "positionals":
|
||||
checkRunWithArgs("posFirst", "a b c d e","""first=@["a", "b", "c"], second=d, third=e""")
|
||||
checkRunWithArgs("posFirst", "a b", "error missing positional args, got: 2, expected at least: 3", code = 1)
|
||||
checkRunWithArgs("posLast", "a b", """first=a, second=b, third=@[]""")
|
||||
checkRunWithArgs("posLastExact", "a b c d e", """first=a, second=b, third=@["c", "d", "e"]""")
|
||||
checkRunWithArgs("posNoMulti", "5 b c", """first=5, second=b, third=c""")
|
||||
checkRunWithArgs("posNoMulti", "5 b c d", """error missing positional args, got: 4, expected: 3""", code = 1)
|
|
@ -70,6 +70,16 @@ hwylCli:
|
|||
a longer mulitline description that will be visible in the subcommand help
|
||||
and it will automatically be "bb"'ed [bold]this is bold text[/]
|
||||
"""
|
||||
# args first, second
|
||||
# or
|
||||
args:
|
||||
# default type is string
|
||||
# only one 'arg' can be the seq[string]
|
||||
# order matters here
|
||||
# by default string
|
||||
inputs:
|
||||
T int
|
||||
second seq[string]
|
||||
flags:
|
||||
^something
|
||||
thing:
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
# TODO: combine this with tests/cli/
|
||||
import std/[
|
||||
unittest
|
||||
unittest,
|
||||
strutils
|
||||
]
|
||||
|
||||
|
||||
import hwylterm, hwylterm/hwylcli
|
||||
|
||||
suite "cli":
|
||||
test "cli":
|
||||
let expected = """[b]test-program[/] [[args...]
|
||||
|
||||
[b cyan]flags[/]:
|
||||
[yellow]-h[/] [magenta]--help [/] []show this help[/]
|
||||
[yellow]-V[/] [magenta]--version[/] []print version[/]
|
||||
"""
|
||||
[bold cyan]flags[/]:
|
||||
[yellow]-h[/yellow] [magenta]--help [/magenta] []show this help[/]
|
||||
[yellow]-V[/yellow] [magenta]--version[/magenta] []print version[/]"""
|
||||
let cli =
|
||||
newHwylCliHelp(
|
||||
header = "[b]test-program[/] [[args...]",
|
||||
flags = [("h","help","show this help",),("V","version","print version")]
|
||||
)
|
||||
check $bb(cli) == $bb(expected)
|
||||
check render(cli) == expected
|
||||
check $bb(render(cli)) == $bb(expected)
|
||||
|
|
Loading…
Add table
Reference in a new issue