Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ vendor/
tests/test_all
tests/test_nested_cmd
tests/test_help
tests/test_argument
19 changes: 7 additions & 12 deletions confutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -182,20 +182,15 @@ func hasOpts(cmd: CmdInfo): bool =
return false

func hasArgs(cmd: CmdInfo): bool =
cmd.opts.len > 0 and cmd.opts[^1].kind == Arg

func firstArgIdx(cmd: CmdInfo): int =
# This will work correctly only if the command has arguments.
result = cmd.opts.len - 1
while result > 0:
if cmd.opts[result - 1].kind != Arg:
return
dec result
for opt in cmd.opts:
if opt.kind == Arg:
return true
false

iterator args(cmd: CmdInfo): OptInfo =
if cmd.hasArgs:
for i in cmd.firstArgIdx ..< cmd.opts.len:
yield cmd.opts[i]
for opt in cmd.opts:
if opt.kind == Arg:
yield opt

func getSubCmdDiscriminator(cmd: CmdInfo): OptInfo =
for i in countdown(cmd.opts.len - 1, 0):
Expand Down
30 changes: 30 additions & 0 deletions tests/help/snapshots/test_argument.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Usage:

test_argument command

Available sub-commands:

test_argument argAfterOpt [OPTIONS]... <arg-after-opt-arg1>

<arg-after-opt-arg1> arg1 desc.

The following options are available:

--arg-after-opt-opt1 opt1 desc [=opt1 default].

test_argument argBeforeOpt [OPTIONS]... <arg-before-opt-arg2>

<arg-before-opt-arg2> arg2 desc.

The following options are available:

--arg-before-opt-opt2 opt2 desc [=opt2 default].

test_argument argAroundOpt [OPTIONS]... <arg-around-opt-arg4> <arg-around-opt-arg5>

<arg-around-opt-arg4> arg4 desc.
<arg-around-opt-arg5> arg5 desc.

The following options are available:

--arg-around-opt-opt3 opt3 desc [=opt3 default].
49 changes: 49 additions & 0 deletions tests/help/test_argument.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import ../../confutils

type
Lvl1Cmd* = enum
noCommand
argAfterOpt
argBeforeOpt
argAroundOpt

TestConf* = object
case cmd* {.
command
defaultValue: Lvl1Cmd.noCommand }: Lvl1Cmd
of Lvl1Cmd.noCommand:
discard
of Lvl1Cmd.argAfterOpt:
opt1* {.
defaultValue: "opt1 default"
desc: "opt1 desc"
name: "arg-after-opt-opt1" }: string
arg1* {.
argument
desc: "arg1 desc"
name: "arg-after-opt-arg1" }: string
of Lvl1Cmd.argBeforeOpt:
arg2* {.
argument
desc: "arg2 desc"
name: "arg-before-opt-arg2" }: string
opt2* {.
defaultValue: "opt2 default"
desc: "opt2 desc"
name: "arg-before-opt-opt2" }: string
of Lvl1Cmd.argAroundOpt:
arg4* {.
argument
desc: "arg4 desc"
name: "arg-around-opt-arg4" }: string
opt3* {.
defaultValue: "opt3 default"
desc: "opt3 desc"
name: "arg-around-opt-opt3" }: string
arg5* {.
argument
desc: "arg5 desc"
name: "arg-around-opt-arg5" }: string

when isMainModule:
let c = TestConf.load(termWidth = int.high)
99 changes: 99 additions & 0 deletions tests/test_argument.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# confutils
# Copyright (c) 2018-2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.

import unittest2, ../confutils, ./help/test_argument

suite "test argument":
test "no command":
let conf = TestConf.load(cmdLine = @[])
check:
conf.cmd == Lvl1Cmd.noCommand

test "pass arg first to argAfterOpt":
let conf = TestConf.load(cmdLine = @[
"argAfterOpt",
"foo",
"--arg-after-opt-opt1=bar"
])
check:
conf.cmd == Lvl1Cmd.argAfterOpt
conf.arg1 == "foo"
conf.opt1 == "bar"

test "pass arg last to argAfterOpt":
let conf = TestConf.load(cmdLine = @[
"argAfterOpt",
"--arg-after-opt-opt1=bar",
"foo"
])
check:
conf.cmd == Lvl1Cmd.argAfterOpt
conf.arg1 == "foo"
conf.opt1 == "bar"

test "pass arg first to argBeforeOpt":
let conf = TestConf.load(cmdLine = @[
"argBeforeOpt",
"foo",
"--arg-before-opt-opt2=bar"
])
check:
conf.cmd == Lvl1Cmd.argBeforeOpt
conf.arg2 == "foo"
conf.opt2 == "bar"

test "pass arg last to argBeforeOpt":
let conf = TestConf.load(cmdLine = @[
"argBeforeOpt",
"--arg-before-opt-opt2=bar",
"foo"
])
check:
conf.cmd == Lvl1Cmd.argBeforeOpt
conf.arg2 == "foo"
conf.opt2 == "bar"

test "pass arg first to argAroundOpt":
let conf = TestConf.load(cmdLine = @[
"argAroundOpt",
"foo",
"bar",
"--arg-around-opt-opt3=baz"
])
check:
conf.cmd == Lvl1Cmd.argAroundOpt
conf.arg4 == "foo"
conf.arg5 == "bar"
conf.opt3 == "baz"

test "pass arg last to argAroundOpt":
let conf = TestConf.load(cmdLine = @[
"argAroundOpt",
"--arg-around-opt-opt3=baz",
"foo",
"bar"
])
check:
conf.cmd == Lvl1Cmd.argAroundOpt
conf.arg4 == "foo"
conf.arg5 == "bar"
conf.opt3 == "baz"

test "pass arg mix to argAroundOpt":
let conf = TestConf.load(cmdLine = @[
"argAroundOpt",
"foo",
"--arg-around-opt-opt3=baz",
"bar"
])
check:
conf.cmd == Lvl1Cmd.argAroundOpt
conf.arg4 == "foo"
conf.arg5 == "bar"
conf.opt3 == "baz"
5 changes: 4 additions & 1 deletion tests/test_help.nim
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ suite "test --help":

test "test test_nested_cmd lvl1Cmd1":
cmdTest("test_nested_cmd", "lvl1Cmd1")

test "test test_nested_cmd lvl1Cmd1 lvl2Cmd2":
cmdTest("test_nested_cmd", "lvl1Cmd1 lvl2Cmd2")

test "test test_argument":
cmdTest("test_argument", "")