6 Commits

14 changed files with 208 additions and 169 deletions
+2
View File
@@ -60,6 +60,8 @@ func main() {
Refer to the unit test for a realistic example: [`cli_test.go`](cli_test.go). Refer to the unit test for a realistic example: [`cli_test.go`](cli_test.go).
## Usage
## API Reference ## API Reference
Key types and methods: Key types and methods:
+88 -51
View File
@@ -14,6 +14,7 @@ type cliParser interface {
getCliArgs(startIndex, endIndex int) (args []string) getCliArgs(startIndex, endIndex int) (args []string)
PrintVersion(specs []string) PrintVersion(specs []string)
PrintUsage() PrintUsage()
FlagIsSet(flag int16) bool
} }
type cliOptionParser interface { type cliOptionParser interface {
@@ -25,7 +26,7 @@ type cliOptionParser interface {
isSet() bool isSet() bool
isHidden() bool isHidden() bool
requiresValue() bool requiresValue() bool
finalCheck(cliValue string) (err error) finalCheck() (err error)
} }
type OptManager interface { type OptManager interface {
@@ -33,7 +34,7 @@ type OptManager interface {
} }
type SpecialValueFunc func(manager OptManager, cliValue string, currentValue any) (value any, err error) type SpecialValueFunc func(manager OptManager, cliValue string, currentValue any) (value any, err error)
type FinalCheckFunc func(cliValue string, currentValue any) (err error) type FinalCheckFunc func(currentValue any) (err error)
type OptReference interface { type OptReference interface {
AddSpecialValue(cliValue string, specialFunc SpecialValueFunc) AddSpecialValue(cliValue string, specialFunc SpecialValueFunc)
@@ -41,20 +42,30 @@ type OptReference interface {
SetHidden(hidden bool) SetHidden(hidden bool)
} }
const (
ResetOnEqualSign = int16(1 << iota) // for some option types, like arrays, the equal signs reset to empty the accumulator of values
)
type CliParser struct { type CliParser struct {
description string description string
version string version string
options []cliOptionParser options []cliOptionParser
argSpecs []argSpec argSpecs []argSpec
cliArgs []string cliArgs []string
flags int16
} }
func (cli *CliParser) Init(argv []string, version string, description string) { func (cli *CliParser) Init(version string, description string, flags ...int16) {
cli.version = version cli.version = version
cli.description = description cli.description = description
cli.cliArgs = argv for _, flag := range flags {
cli.flags |= flag
}
} }
func (cli *CliParser) FlagIsSet(flag int16) bool {
return (cli.flags & flag) == flag
}
func (cli *CliParser) GetOption(name string) (ref OptReference) { func (cli *CliParser) GetOption(name string) (ref OptReference) {
var opt cliOptionParser var opt cliOptionParser
if strings.HasPrefix(name, "-") { if strings.HasPrefix(name, "-") {
@@ -158,17 +169,12 @@ func (cli *CliParser) addHelpAndVersion() {
} }
func (cli *CliParser) Parse() (err error) { func (cli *CliParser) parseOptions() (commandArgs []string, err error) {
var arg string // var commandArgs []string
var i int
var args []string
var optionsAllowed bool = true var optionsAllowed bool = true
cli.addHelpAndVersion()
// first parse options and collect argument in the args array
skipNext := false skipNext := false
for i, arg = range cli.cliArgs[1:] { for i, arg := range cli.cliArgs[1:] {
if skipNext { if skipNext {
skipNext = false skipNext = false
} else { } else {
@@ -182,50 +188,81 @@ func (cli *CliParser) Parse() (err error) {
break break
} }
} else { } else {
args = append(args, arg) commandArgs = append(commandArgs, arg)
} }
} }
} }
return
}
func (cli *CliParser) checkOptionValues() (err error) {
for _, opt := range cli.options {
if err = opt.finalCheck(); err != nil {
break
}
}
return
}
func (cli *CliParser) parseCommandArgs(commandArgs []string) (err error) {
var n int
i := 0
// acquire arguments as required by the argSpecs
specIndex := -1
for index, argSpec := range cli.argSpecs {
specIndex = index
if n, err = argSpec.parse(cli, specIndex, commandArgs, i); err != nil {
break
}
i += n
if i >= len(commandArgs) {
break
}
}
// check if there are remaining arg-specs that require a value
if err == nil {
if i < len(commandArgs) {
err = fmt.Errorf("too many arguments: %d allowed", i)
} else {
specIndex++
if specIndex < len(cli.argSpecs) {
// skip all non required args
for _, spec := range cli.argSpecs[specIndex:] {
if !spec.getBase().required {
specIndex++
}
}
}
// return error if there are remaining arg-specs that require a value
if specIndex < len(cli.argSpecs) {
err = errTooFewArguments(len(cli.argSpecs))
}
}
}
return
}
func (cli *CliParser) Parse(argv []string) (err error) {
var commandArgs []string
cli.cliArgs = argv
cli.addHelpAndVersion()
// first parse options and collect command arguments in the args array
if commandArgs, err = cli.parseOptions(); err != nil {
return
}
// do final checks
if err = cli.checkOptionValues(); err != nil {
return
}
// then parse collected arguments // then parse collected arguments
if err == nil { err = cli.parseCommandArgs(commandArgs)
var n int
i = 0
// acquire arguments as required by the argSpecs
specIndex := -1
for index, argSpec := range cli.argSpecs {
specIndex = index
if n, err = argSpec.parse(cli, specIndex, args, i); err != nil {
break
}
i += n
if i >= len(args) {
break
}
}
// check if there are remaining arg-specs that require a value
if err == nil {
if i < len(args) {
err = fmt.Errorf("too many arguments: %d allowed", i)
} else {
specIndex++
if specIndex < len(cli.argSpecs) {
// skip all non required args
for _, spec := range cli.argSpecs[specIndex:] {
if !spec.getBase().required {
specIndex++
}
}
}
// return error if there are remaining arg-specs that require a value
if specIndex < len(cli.argSpecs) {
err = errTooFewArguments(len(cli.argSpecs))
}
}
}
}
return return
} }
+30 -41
View File
@@ -6,7 +6,7 @@ import (
"testing" "testing"
) )
const version = `$VER:ddt-ocr,1.0.1,2025-11-23,celestino.amoroso@gmail.com:$` const version = `$VER:ddt-ocr,2.0.0,2026-03-19,celestino.amoroso@gmail.com:$`
type GlobalData struct { type GlobalData struct {
config string config string
@@ -33,7 +33,7 @@ func TestVerbose(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
if err := cli.Parse(); err != nil { if err := cli.Parse(commonArgs()); err != nil {
t.Error(err) t.Error(err)
} else if gd.verbose != 3 { } else if gd.verbose != 3 {
t.Errorf("Expected verbose level 3, got %d", gd.verbose) t.Errorf("Expected verbose level 3, got %d", gd.verbose)
@@ -134,7 +134,7 @@ Option: version, Type: n/a, Value: <nil>
return return
} }
tracer := NewSimpleOptionTracer(&sb) tracer := NewSimpleOptionTracer(&sb)
if err := cli.Parse(); err == nil { if err := cli.Parse(commonArgs()); err == nil {
cli.TraceOptions(tracer) cli.TraceOptions(tracer)
if sb.String() != expectedOutput { if sb.String() != expectedOutput {
t.Errorf("Parsed options do not match expected list.\nGot:\n%q\nExpected:\n%q", sb.String(), expectedOutput) t.Errorf("Parsed options do not match expected list.\nGot:\n%q\nExpected:\n%q", sb.String(), expectedOutput)
@@ -144,8 +144,8 @@ Option: version, Type: n/a, Value: <nil>
} }
} }
func initCli(cli *CliParser, gd *GlobalData) (err error) { func commonArgs() []string {
args := []string{ return []string{
"ddt-ocr", "ddt-ocr",
"--log", "all", "--log", "all",
"-t", "-t",
@@ -157,7 +157,10 @@ func initCli(cli *CliParser, gd *GlobalData) (err error) {
"--input-name=my-scan.pdf", "--input-name=my-scan.pdf",
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt", "scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
} }
cli.Init(args, version, "cli-test") }
func initCli(cli *CliParser, gd *GlobalData) (err error) {
cli.Init(version, "cli-test")
err = gd.addOptions(cli) err = gd.addOptions(cli)
return return
} }
@@ -168,8 +171,8 @@ func TestOptErrorUnknownOption(t *testing.T) {
var cli CliParser var cli CliParser
var gd GlobalData var gd GlobalData
if err := initCliUnknownOption(&cli, &gd, unknownOption); err == nil { if err := initCliUnknownOption(&cli, &gd); err == nil {
if err = cli.Parse(); err != nil { if err = cli.Parse(commonBadArgs(unknownOption)); err != nil {
if err.Error() != expectedErr.Error() { if err.Error() != expectedErr.Error() {
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr) t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
} }
@@ -182,13 +185,15 @@ func TestOptErrorUnknownOption(t *testing.T) {
} }
} }
func initCliUnknownOption(cli *CliParser, gd *GlobalData, option string) (err error) { func commonBadArgs(option string) []string {
args := []string{ return []string{
"ddt-ocr", "ddt-ocr",
option, option,
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt", "scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
} }
cli.Init(args, version, "cli-test") }
func initCliUnknownOption(cli *CliParser, gd *GlobalData) (err error) {
cli.Init(version, "cli-test")
err = gd.addOptions(cli) err = gd.addOptions(cli)
return return
} }
@@ -199,8 +204,8 @@ func TestOptErrorMissingOptionValue(t *testing.T) {
var cli CliParser var cli CliParser
var gd GlobalData var gd GlobalData
if err := initCliMissingOptionValue(&cli, &gd, missingValueOption); err == nil { if err := initCliMissingOptionValue(&cli, &gd); err == nil {
if err = cli.Parse(); err != nil { if err = cli.Parse(commonBadArgs(missingValueOption)); err != nil {
if err.Error() != expectedErr.Error() { if err.Error() != expectedErr.Error() {
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr) t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
} }
@@ -213,25 +218,20 @@ func TestOptErrorMissingOptionValue(t *testing.T) {
} }
} }
func initCliMissingOptionValue(cli *CliParser, gd *GlobalData, option string) (err error) { func initCliMissingOptionValue(cli *CliParser, gd *GlobalData) (err error) {
args := []string{ cli.Init(version, "cli-test")
"ddt-ocr",
option,
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
}
cli.Init(args, version, "cli-test")
err = gd.addOptions(cli) err = gd.addOptions(cli)
return return
} }
func TestOptErrorInvalidOptionValue(t *testing.T) { func TestOptErrorInvalidOptionValue(t *testing.T) {
const missingInvalidValueOption = "--page" const missingInvalidValueOption = "--page"
var expectedErr = errInvalidOptionValue("page", "some", "num-array") var expectedErr = errInvalidOptionValue("page", "scan1.pdf", "num-array")
var cli CliParser var cli CliParser
var gd GlobalData var gd GlobalData
if err := initCliInvalidOptionValue(&cli, &gd, missingInvalidValueOption); err == nil { if err := initCliInvalidOptionValue(&cli, &gd); err == nil {
if err = cli.Parse(); err != nil { if err = cli.Parse(commonBadArgs(missingInvalidValueOption)); err != nil {
if err.Error() != expectedErr.Error() { if err.Error() != expectedErr.Error() {
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr) t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
} }
@@ -244,13 +244,8 @@ func TestOptErrorInvalidOptionValue(t *testing.T) {
} }
} }
func initCliInvalidOptionValue(cli *CliParser, gd *GlobalData, option string) (err error) { func initCliInvalidOptionValue(cli *CliParser, gd *GlobalData) (err error) {
args := []string{ cli.Init(version, "cli-test")
"ddt-ocr",
option, "some",
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
}
cli.Init(args, version, "cli-test")
err = gd.addOptions(cli) err = gd.addOptions(cli)
return return
} }
@@ -262,7 +257,7 @@ func TestArgErrorMissingRequired(t *testing.T) {
var gd GlobalData var gd GlobalData
if err := initCliMissingRequiredArg(&cli, &gd); err == nil { if err := initCliMissingRequiredArg(&cli, &gd); err == nil {
if err = cli.Parse(); err != nil { if err = cli.Parse([]string{"ddt-ocr"}); err != nil {
if err.Error() != expectedErr.Error() { if err.Error() != expectedErr.Error() {
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr) t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
} }
@@ -276,15 +271,12 @@ func TestArgErrorMissingRequired(t *testing.T) {
} }
func initCliMissingRequiredArg(cli *CliParser, gd *GlobalData) (err error) { func initCliMissingRequiredArg(cli *CliParser, gd *GlobalData) (err error) {
args := []string{ cli.Init(version, "cli-test")
"ddt-ocr",
}
cli.Init(args, version, "cli-test")
err = gd.addOptions(cli) err = gd.addOptions(cli)
return return
} }
func TestArgErrorRepeat(t *testing.T) { func TestArgErrorRepeatProperty(t *testing.T) {
var expectedErr = fmt.Errorf(`repeat property already set for arg <other>`) var expectedErr = fmt.Errorf(`repeat property already set for arg <other>`)
var cli CliParser var cli CliParser
var gd GlobalData var gd GlobalData
@@ -299,17 +291,14 @@ func TestArgErrorRepeat(t *testing.T) {
} }
func initCliRepeatArg(cli *CliParser, gd *GlobalData) (err error) { func initCliRepeatArg(cli *CliParser, gd *GlobalData) (err error) {
args := []string{ cli.Init(version, "cli-test")
"ddt-ocr",
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
}
cli.Init(args, version, "cli-test")
if err = gd.addOptions(cli); err == nil { if err = gd.addOptions(cli); err == nil {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
err = r.(error) err = r.(error)
} }
}() }()
// This will raise error because we can't declare two args array
cli.AddStringArrayArg("other", true, nil, "other args") cli.AddStringArrayArg("other", true, nil, "other args")
cli.AddStringArrayArg("other2", true, nil, "other args 2") cli.AddStringArrayArg("other2", true, nil, "other args 2")
} }
+1 -1
View File
@@ -1,3 +1,3 @@
module git.portale-stac.it/go-pkg/cli module git.portale-stac.it/go-pkg/cli/v2
go 1.25.4 go 1.25.4
+3 -3
View File
@@ -153,13 +153,13 @@ func (opt *cliOptionBase) fetchOptionValue(parser cliParser, argIndex int, value
return return
} }
func (opt *cliOptionBase) finalCheck(cliValue string) (err error) { func (opt *cliOptionBase) finalCheck() (err error) {
return return
} }
func (opt *cliOptionBase) checkValue(cliValue string, value any) (err error) { func (opt *cliOptionBase) checkValue(value any) (err error) {
if opt.finalCheckFunc != nil { if opt.finalCheckFunc != nil {
err = opt.finalCheckFunc(cliValue, value) err = opt.finalCheckFunc(value)
} }
return return
} }
+2 -2
View File
@@ -67,9 +67,9 @@ func (opt *cliOptionBool) parse(parser cliParser, argIndex int, valuePtr *string
return return
} }
func (opt *cliOptionBool) finalCheck(cliValue string) (err error) { func (opt *cliOptionBool) finalCheck() (err error) {
currentValue, _ := opt.getTargetVar() currentValue, _ := opt.getTargetVar()
return opt.getBase().checkValue(cliValue, currentValue) return opt.getBase().checkValue(currentValue)
} }
func (cli *CliParser) AddBoolOpt(name, short string, targetVar *bool, description string, aliases ...string) OptReference { func (cli *CliParser) AddBoolOpt(name, short string, targetVar *bool, description string, aliases ...string) OptReference {
+3 -3
View File
@@ -75,7 +75,7 @@ func (opt *cliOptionIntArray) parse(parser cliParser, argIndex int, valuePtr *st
err = errInvalidOptionValue(opt.name, boxedValue, "num-array") err = errInvalidOptionValue(opt.name, boxedValue, "num-array")
} }
} else { } else {
if !opt.alreadySeen { if !opt.alreadySeen || (valuePtr != nil && parser.FlagIsSet(ResetOnEqualSign)) {
*opt.targetVar = []int{} *opt.targetVar = []int{}
opt.alreadySeen = true opt.alreadySeen = true
} }
@@ -97,9 +97,9 @@ func (opt *cliOptionIntArray) parse(parser cliParser, argIndex int, valuePtr *st
return return
} }
func (opt *cliOptionIntArray) finalCheck(cliValue string) (err error) { func (opt *cliOptionIntArray) finalCheck() (err error) {
currentValue, _ := opt.getTargetVar() currentValue, _ := opt.getTargetVar()
return opt.getBase().checkValue(cliValue, currentValue) return opt.getBase().checkValue(currentValue)
} }
func (cli *CliParser) AddIntArrayOpt(name, short string, targetVar *[]int, defaultValue []int, description string, aliases ...string) OptReference { func (cli *CliParser) AddIntArrayOpt(name, short string, targetVar *[]int, defaultValue []int, description string, aliases ...string) OptReference {
+2 -2
View File
@@ -57,9 +57,9 @@ func (opt *cliOptionInt) parse(parser cliParser, argIndex int, valuePtr *string)
} }
return return
} }
func (opt *cliOptionInt) finalCheck(cliValue string) (err error) { func (opt *cliOptionInt) finalCheck() (err error) {
currentValue, _ := opt.getTargetVar() currentValue, _ := opt.getTargetVar()
return opt.getBase().checkValue(cliValue, currentValue) return opt.getBase().checkValue(currentValue)
} }
func (cli *CliParser) AddIntOpt(name, short string, targetVar *int, defaultValue int, description string, aliases ...string) OptReference { func (cli *CliParser) AddIntOpt(name, short string, targetVar *int, defaultValue int, description string, aliases ...string) OptReference {
+2 -2
View File
@@ -53,9 +53,9 @@ func (opt *cliOptionMulti) parse(parser cliParser, argIndex int, valuePtr *strin
value = "true" value = "true"
return return
} }
func (opt *cliOptionMulti) finalCheck(cliValue string) (err error) { func (opt *cliOptionMulti) finalCheck() (err error) {
currentValue, _ := opt.getTargetVar() currentValue, _ := opt.getTargetVar()
return opt.getBase().checkValue(cliValue, currentValue) return opt.getBase().checkValue(currentValue)
} }
func (cli *CliParser) AddMultiOpt(name, short string, targetVar *int, defaultValue int, description string, aliases ...string) OptReference { func (cli *CliParser) AddMultiOpt(name, short string, targetVar *int, defaultValue int, description string, aliases ...string) OptReference {
+2 -2
View File
@@ -85,7 +85,7 @@ func (cli *CliParser) AddStringArrayOpt(name, short string, targetVar *[]string,
return opt return opt
} }
func (opt *cliOptionStringArray) finalCheck(cliValue string) (err error) { func (opt *cliOptionStringArray) finalCheck() (err error) {
currentValue, _ := opt.getTargetVar() currentValue, _ := opt.getTargetVar()
return opt.getBase().checkValue(cliValue, currentValue) return opt.getBase().checkValue(currentValue)
} }
+2 -2
View File
@@ -89,9 +89,9 @@ func (opt *cliOptionStringMap) parse(parser cliParser, argIndex int, valuePtr *s
return return
} }
func (opt *cliOptionStringMap) finalCheck(cliValue string) (err error) { func (opt *cliOptionStringMap) finalCheck() (err error) {
currentValue, _ := opt.getTargetVar() currentValue, _ := opt.getTargetVar()
return opt.getBase().checkValue(cliValue, currentValue) return opt.getBase().checkValue(currentValue)
} }
func (cli *CliParser) AddStringMapOpt(name, short string, targetVar *map[string]string, defaultValue map[string]string, description string, aliases ...string) OptReference { func (cli *CliParser) AddStringMapOpt(name, short string, targetVar *map[string]string, defaultValue map[string]string, description string, aliases ...string) OptReference {
+2 -2
View File
@@ -55,9 +55,9 @@ func (opt *cliOptionString) parse(parser cliParser, argIndex int, valuePtr *stri
} }
return return
} }
func (opt *cliOptionString) finalCheck(cliValue string) (err error) { func (opt *cliOptionString) finalCheck() (err error) {
currentValue, _ := opt.getTargetVar() currentValue, _ := opt.getTargetVar()
return opt.getBase().checkValue(cliValue, currentValue) return opt.getBase().checkValue(currentValue)
} }
func (cli *CliParser) AddStringOpt(name, short string, targetVar *string, defaultValue string, description string, aliases ...string) OptReference { func (cli *CliParser) AddStringOpt(name, short string, targetVar *string, defaultValue string, description string, aliases ...string) OptReference {
+67 -52
View File
@@ -26,8 +26,8 @@ func TestOneOptWithEqual(t *testing.T) {
"--color=blue", "--color=blue",
} }
cli.Init(args, "1.0.0", "TestOneOptWithEqual description") cli.Init("1.0.0", "TestOneOptWithEqual description")
if err := cli.Parse(); err != nil { if err := cli.Parse(args); err != nil {
t.Error(err) t.Error(err)
} else if color != "blue" { } else if color != "blue" {
t.Errorf("Expected color blue, got %q", color) t.Errorf("Expected color blue, got %q", color)
@@ -37,7 +37,7 @@ func TestOneOptWithEqual(t *testing.T) {
func addBoxOption(cli *CliParser, boxPtr *[]int) { func addBoxOption(cli *CliParser, boxPtr *[]int) {
// Define options // Define options
optRef := cli.AddIntArrayOpt("box", "b", boxPtr, []int{1, 2, 3, 4}, "Box spec: Left,Width,Top,Height; provide 4 int values") optRef := cli.AddIntArrayOpt("box", "b", boxPtr, []int{1, 2, 3, 4}, "Box spec: Left,Width,Top,Height; provide 4 int values")
optRef.OnFinalCheck(func(cliValue string, currentValue any) (err error) { optRef.OnFinalCheck(func(currentValue any) (err error) {
if array, ok := currentValue.([]int); ok { if array, ok := currentValue.([]int); ok {
if len(array) != 4 { if len(array) != 4 {
err = fmt.Errorf("--box option requires exactly 4 items, %d provided", len(array)) err = fmt.Errorf("--box option requires exactly 4 items, %d provided", len(array))
@@ -49,8 +49,34 @@ func addBoxOption(cli *CliParser, boxPtr *[]int) {
}) })
} }
func TestIntArrayOpt(t *testing.T) { func testIntArrayOptOk(t *testing.T, n int, args []string, flags ...int16) {
var box []int var box []int
var cli CliParser
t.Logf("Arg set n. %d", n)
addBoxOption(&cli, &box)
cli.Init("1.0.0", "TestIntArrayOpt description", flags...)
if err := cli.Parse(args); err != nil {
t.Error(err)
} else if len(box) != 4 {
t.Errorf(`Expected 4 items, got %d`, len(box))
}
}
func testIntArrayOptKo(t *testing.T, n int, args []string, msg string, flags ...int16) {
var box []int
var cli CliParser
t.Logf("Arg set n. %d", n)
addBoxOption(&cli, &box)
cli.Init("1.0.0", "TestIntArrayOpt description", flags...)
if err := cli.Parse(args); err == nil {
t.Errorf("Expected error, got nil")
} else if err.Error() != msg {
t.Errorf(`Invalid error: %q; expected: %q`, err.Error(), msg)
}
}
func TestIntArrayOpt(t *testing.T) {
var args []string
// Always recover from panic to return error before adding options // Always recover from panic to return error before adding options
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@@ -61,68 +87,57 @@ func TestIntArrayOpt(t *testing.T) {
} }
}() }()
args_1 := []string{ n := 0
n++
args = []string{
"TestIntArrayOpt", "TestIntArrayOpt",
"--box=100,200,150,450", "--box=100,200,150,450",
} }
testIntArrayOptOk(t, n, args)
if true { n++
var cli CliParser args = []string{
t.Logf("Arg set n. 1")
addBoxOption(&cli, &box)
cli.Init(args_1, "1.0.0", "TestIntArrayOpt description")
if err := cli.Parse(); err != nil {
t.Error(err)
} else if len(box) != 4 {
t.Errorf(`Expected 4 items, got %d`, len(box))
}
}
args_2 := []string{
"TestIntArrayOpt", "TestIntArrayOpt",
"--box=100,200,150", "--box=100,200,150",
} }
if true { testIntArrayOptKo(t, n, args, "--box option requires exactly 4 items, 3 provided")
var cli CliParser
t.Logf("Arg set n. 2")
addBoxOption(&cli, &box)
cli.Init(args_2, "1.0.0", "TestIntArrayOpt description")
if err := cli.Parse(); err == nil {
t.Errorf("Expected error, got nil")
} else if err.Error() != "--box option requires exactly 4 items, 3 provided" {
t.Errorf(`Invalid error: %q; expected: "--box option requires exactly 4 items, 3 provided"`, err.Error())
}
}
args_3 := []string{ n++
args = []string{
"TestIntArrayOpt", "TestIntArrayOpt",
"--box", "100,200,150,450", "--box", "100,200,150,450",
} }
if true { testIntArrayOptOk(t, n, args)
var cli CliParser
t.Logf("Arg set n. 3")
addBoxOption(&cli, &box)
cli.Init(args_3, "1.0.0", "TestIntArrayOpt description")
if err := cli.Parse(); err != nil {
t.Error(err)
} else if len(box) != 4 {
t.Errorf(`Expected 4 items, got %d`, len(box))
}
}
args_4 := []string{ n++
args = []string{
"TestIntArrayOpt", "TestIntArrayOpt",
"--box", "--box",
} }
if true { testIntArrayOptKo(t, n, args, `option "box" requires a value`)
var cli CliParser
t.Logf("Arg set n. 4") n++
addBoxOption(&cli, &box) args = []string{
cli.Init(args_4, "1.0.0", "TestIntArrayOpt description") "TestIntArrayOpt",
if err := cli.Parse(); err == nil { "--box", "100,200,150",
t.Errorf("Expected error, got nil") "--box", "450",
} else if err.Error() != `option "box" requires a value` {
t.Errorf(`Invalid error - Expected: 'option "box" requires a value'; got '%s'`, err.Error())
}
} }
testIntArrayOptOk(t, n, args)
n++
args = []string{
"TestIntArrayOpt",
"--box", "100,200,150",
"--box", "450,12",
}
testIntArrayOptKo(t, n, args, "--box option requires exactly 4 items, 5 provided")
n++
args = []string{
"TestIntArrayOpt",
"--box", "100,200,150",
"--box=100,200,150,450",
}
testIntArrayOptOk(t, n, args, ResetOnEqualSign)
} }
+2 -6
View File
@@ -104,16 +104,12 @@ func (cli *CliParser) parseArg(arg string, index int) (skipNextArg bool, err err
} }
for i, optName := range opts { for i, optName := range opts {
if opt := cli.findOptionByArg(dashes + optName); opt != nil { if opt := cli.findOptionByArg(dashes + optName); opt != nil {
var optValue string
if equalPresent && i == len(opts)-1 { if equalPresent && i == len(opts)-1 {
_, optValue, err = opt.parse(cli, index, &value) _, _, err = opt.parse(cli, index, &value)
} else if i < len(opts)-1 && opt.requiresValue() { } else if i < len(opts)-1 && opt.requiresValue() {
err = errMissingOptionValue(dashes + optName) err = errMissingOptionValue(dashes + optName)
} else { } else {
skipNextArg, optValue, err = opt.parse(cli, index, nil) skipNextArg, _, err = opt.parse(cli, index, nil)
}
if err == nil && i == len(opts)-1 {
err = opt.finalCheck(optValue)
} }
} else { } else {
err = errUnknownOption(dashes + optName) err = errUnknownOption(dashes + optName)