From 4b7297a05f6e6b42d0160ecfde84f38b233e58c9 Mon Sep 17 00:00:00 2001 From: Brian Hendriks Date: Tue, 3 Sep 2024 16:02:15 -0700 Subject: [PATCH 1/3] support optional string arguments in the middle of command line arguments --- go/libraries/utils/argparser/parser.go | 33 ++++++++++-- go/libraries/utils/argparser/parser_test.go | 56 +++++++++++++++++++-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/go/libraries/utils/argparser/parser.go b/go/libraries/utils/argparser/parser.go index 34e4c8b50b..ef556bbd4f 100644 --- a/go/libraries/utils/argparser/parser.go +++ b/go/libraries/utils/argparser/parser.go @@ -343,6 +343,22 @@ func (ap *ArgParser) Parse(args []string) (*ArgParseResults, error) { return &ArgParseResults{namedArgs, positionalArgs, ap, positionalArgsSeparatorIndex}, nil } +func (ap *ArgParser) isOptionOrFlag(s string) bool { + if len(s) == 0 { + return false + } else if s[0] != '-' { + return false + } + s = s[1:] + + if len(s) >= 1 && s[0] == '-' { + s = s[1:] + } + + _, ok := ap.nameOrAbbrevToOpt[s] + return ok +} + func (ap *ArgParser) parseToken(args []string, index int, positionalArgs []string, namedArgs map[string]string) (newIndex int, newPositionalArgs []string, newNamedArgs map[string]string, err error) { arg := args[index] @@ -387,19 +403,28 @@ func (ap *ArgParser) parseToken(args []string, index int, positionalArgs []strin } if value == nil { - index++ + next := index + 1 valueStr := "" - if index >= len(args) { + if next >= len(args) { if opt.OptType != OptionalEmptyValue { return 0, nil, nil, errors.New("error: no value for option `" + opt.Name + "'") } } else { if opt.AllowMultipleOptions { - list := getListValues(args[index:]) + list := getListValues(args[next:]) valueStr = strings.Join(list, ",") index += len(list) - 1 } else { - valueStr = args[index] + nextArg := args[next] + if opt.OptType == OptionalEmptyValue { + if !(nextArg == "--" || ap.isOptionOrFlag(nextArg)) { + valueStr = args[next] + index = next + } + } else { + valueStr = args[next] + index = next + } } } value = &valueStr diff --git a/go/libraries/utils/argparser/parser_test.go b/go/libraries/utils/argparser/parser_test.go index 16538a7a24..5a45b83250 100644 --- a/go/libraries/utils/argparser/parser_test.go +++ b/go/libraries/utils/argparser/parser_test.go @@ -15,13 +15,21 @@ package argparser import ( - "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func createParserWithOptionalArgs() *ArgParser { + ap := NewArgParserWithMaxArgs("test", 16) + ap.SupportsFlag("flag", "f", "flag") + ap.SupportsString("param", "p", "param", "") + ap.SupportsOptionalString("optional", "o", "optional", "") + + return ap +} + func TestArgParser(t *testing.T) { tests := []struct { ap *ArgParser @@ -94,10 +102,52 @@ func TestArgParser(t *testing.T) { []string{}, }, { - NewArgParserWithMaxArgs("test", 1), + createParserWithOptionalArgs(), []string{"foo", "bar"}, - errors.New("error: test has too many positional arguments. Expected at most 1, found 2: foo, bar"), + nil, map[string]string{}, + []string{"foo", "bar"}, + }, + { + createParserWithOptionalArgs(), + []string{"-o", "-f", "foo", "bar"}, + nil, + map[string]string{"flag": "", "optional": ""}, + []string{"foo", "bar"}, + }, + { + createParserWithOptionalArgs(), + []string{"-o", "optional value", "-f", "foo", "bar"}, + nil, + map[string]string{"flag": "", "optional": "optional value"}, + []string{"foo", "bar"}, + }, + { + createParserWithOptionalArgs(), + []string{"-o", "--", "foo", "bar"}, + nil, + map[string]string{"optional": ""}, + []string{"foo", "bar"}, + }, + { + createParserWithOptionalArgs(), + []string{"-p", "value", "-o"}, + nil, + map[string]string{"param": "value", "optional": ""}, + []string{}, + }, + { + createParserWithOptionalArgs(), + []string{"-p", "value", "-o", "--"}, + nil, + map[string]string{"param": "value", "optional": ""}, + []string{}, + }, + { + createParserWithOptionalArgs(), + []string{"-o", "-p", "value"}, + nil, + map[string]string{"param": "value", "optional": ""}, []string{}, }, } From ce0606e537f38f5b5a911dda8a7296ed3ed3e832 Mon Sep 17 00:00:00 2001 From: Brian Hendriks Date: Tue, 3 Sep 2024 16:56:20 -0700 Subject: [PATCH 2/3] fix --- go/libraries/utils/argparser/parser.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/libraries/utils/argparser/parser.go b/go/libraries/utils/argparser/parser.go index ef556bbd4f..218f4ca289 100644 --- a/go/libraries/utils/argparser/parser.go +++ b/go/libraries/utils/argparser/parser.go @@ -413,7 +413,7 @@ func (ap *ArgParser) parseToken(args []string, index int, positionalArgs []strin if opt.AllowMultipleOptions { list := getListValues(args[next:]) valueStr = strings.Join(list, ",") - index += len(list) - 1 + index += len(list) } else { nextArg := args[next] if opt.OptType == OptionalEmptyValue { @@ -426,8 +426,8 @@ func (ap *ArgParser) parseToken(args []string, index int, positionalArgs []strin index = next } } + value = &valueStr } - value = &valueStr } if opt.Validator != nil { From 0e451b73c18177e5b8c04990b7a6019df1e47e63 Mon Sep 17 00:00:00 2001 From: Brian Hendriks Date: Tue, 3 Sep 2024 17:25:32 -0700 Subject: [PATCH 3/3] fix --- go/libraries/utils/argparser/parser.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go/libraries/utils/argparser/parser.go b/go/libraries/utils/argparser/parser.go index 218f4ca289..8e10daa829 100644 --- a/go/libraries/utils/argparser/parser.go +++ b/go/libraries/utils/argparser/parser.go @@ -438,6 +438,10 @@ func (ap *ArgParser) parseToken(args []string, index int, positionalArgs []strin } } + if value == nil { + value = new(string) + } + namedArgs[opt.Name] = *value return index, positionalArgs, namedArgs, nil }