add support for enums and count flags

This commit is contained in:
Daylin Morgan 2024-11-10 03:36:19 -06:00
parent 8b37c0dad9
commit 77cfae4d26
Signed by: daylin
GPG key ID: 950D13E9719334AD
2 changed files with 44 additions and 10 deletions

View file

@ -121,6 +121,9 @@ proc `$`*(cli: HwylCliHelp): string =
# ---------------------------------------- # ----------------------------------------
type type
Count* = object
val*: int
CliSetting = enum CliSetting = enum
NoHelpFlag, NoArgsShowHelp NoHelpFlag, NoArgsShowHelp
BuiltinFlag = object BuiltinFlag = object
@ -453,6 +456,7 @@ func parseCliBody(body: NimNode, name = ""): CliCfg =
if result.name == "": if result.name == "":
error "missing required option: name" error "missing required option: name"
# TODO: validate "required" flags exist here
result.addBuiltinFlags() result.addBuiltinFlags()
func flagToTuple(f: CliFlag | BuiltinFlag): NimNode = func flagToTuple(f: CliFlag | BuiltinFlag): NimNode =
@ -523,6 +527,22 @@ proc parse*(p: OptParser, key: string, val: string, target: var int) =
"failed to parse value for [b]" & key & "[/] as integer: [b]" & val "failed to parse value for [b]" & key & "[/] as integer: [b]" & val
) )
macro enumNames(a: typed): untyped =
## unexported macro copied from std/enumutils
result = newNimNode(nnkBracket)
for ai in a.getType[1][1..^1]:
assert ai.kind == nnkSym
result.add newLit ai.strVal
proc parse*[E: enum](p: OptParser, key: string, val: string, target: var E) =
try:
target = parseEnum[E](val)
except:
let choices = enumNames(E).join(",")
hwylCliError(
"failed to parse value for [b]" & key & "[/] as enum: [b]" & val & "[/], expected one of: " & choices
)
proc parse*(p: OptParser, key: string, val: string, target: var float) = proc parse*(p: OptParser, key: string, val: string, target: var float) =
try: try:
target = parseFloat(val) target = parseFloat(val)
@ -531,11 +551,14 @@ proc parse*(p: OptParser, key: string, val: string, target: var float) =
"failed to parse value for [b]" & key & "[/] as float: [b]" & val "failed to parse value for [b]" & key & "[/] as float: [b]" & val
) )
proc parse[T](p: OptParser, key: string, val: string, target: var seq[T]) = proc parse*[T](p: OptParser, key: string, val: string, target: var seq[T]) =
var parsed: T var parsed: T
parse(p, key, val, parsed) parse(p, key, val, parsed)
target.add parsed target.add parsed
proc parse*(p: OptParser, key: string, val: string, target: var Count) =
inc target.val
func shortLongCaseStmt(cfg: CliCfg, printHelpName: NimNode, version: NimNode): NimNode = func shortLongCaseStmt(cfg: CliCfg, printHelpName: NimNode, version: NimNode): NimNode =
var caseStmt = nnkCaseStmt.newTree(ident("key")) var caseStmt = nnkCaseStmt.newTree(ident("key"))
caseStmt.add nnkOfBranch.newTree(newLit(""), quote do: hwylCliError("empty flag not supported currently")) caseStmt.add nnkOfBranch.newTree(newLit(""), quote do: hwylCliError("empty flag not supported currently"))
@ -567,15 +590,18 @@ func shortLongCaseStmt(cfg: CliCfg, printHelpName: NimNode, version: NimNode): N
func isBool(f: CliFlag): bool = func isBool(f: CliFlag): bool =
f.typeNode == ident"bool" f.typeNode == ident"bool"
func isCount(f: CliFlag): bool =
f.typeNode == ident"Count"
func getNoVals(cfg: CliCfg): tuple[long: NimNode, short: NimNode] = func getNoVals(cfg: CliCfg): tuple[long: NimNode, short: NimNode] =
let boolFlags = cfg.flags.filterIt(it.isBool) let flagFlags = cfg.flags.filterIt(it.isBool or it.isCount)
let long = let long =
nnkBracket.newTree( nnkBracket.newTree(
(boolFlags.mapIt(it.long) & cfg.builtinFlags.mapIt(it.long)).filterIt(it != "").mapIt(newLit(it)) (flagFlags.mapIt(it.long) & cfg.builtinFlags.mapIt(it.long)).filterIt(it != "").mapIt(newLit(it))
) )
let short = let short =
nnkCurly.newTree( nnkCurly.newTree(
(boolFlags.mapIt(it.short) & cfg.builtinFlags.mapIt(it.short)).filterIt(it != '\x00').mapIt(newLit(it)) (flagFlags.mapIt(it.short) & cfg.builtinFlags.mapIt(it.short)).filterIt(it != '\x00').mapIt(newLit(it))
) )
result = (nnkPrefix.newTree(ident"@",long), short) result = (nnkPrefix.newTree(ident"@",long), short)
@ -586,7 +612,7 @@ func setFlagVars(cfg: CliCfg): NimNode =
) )
) )
func literalFlags(f: CliFlag): NimNode = func literalFlags(f: CliFlag): NimNode =
var flags: seq[string] var flags: seq[string]
if f.short != '\x00': flags.add "[b]" & "-" & $f.short & "[/]" if f.short != '\x00': flags.add "[b]" & "-" & $f.short & "[/]"
if f.long != "": flags.add "[b]" & "--" & f.long & "[/]" if f.long != "": flags.add "[b]" & "--" & f.long & "[/]"

View file

@ -1,6 +1,10 @@
import std/strformat import std/strformat
import hwylterm/hwylcli import hwylterm/hwylcli
type
Color = enum
red, blue, green
hwylCli: hwylCli:
name "example" name "example"
V "0.1.0" V "0.1.0"
@ -20,14 +24,18 @@ hwylCli:
subcommands: subcommands:
--- one --- one
... "the first subcommand" ... "the first subcommand"
required flag
flags: flags:
`long-flag` "some help" color:
flag: T Color
? "some other help" ? "a color (red, green, blue)"
verbose:
T Count
? "a count flag"
- v
run: run:
echo "hello from `example one` command!" echo "hello from `example one` command!"
echo "long-flag and flag are: " & `long-flag` & "," & `flag` & " by default strings" echo fmt"{color=}"
echo fmt"{verbose=}"
--- two --- two
... """ ... """