From de746e6c69fd7d4f191d9b004ae94e6d3d3b4eca Mon Sep 17 00:00:00 2001 From: Raghu Rajagopalan Date: Fri, 6 Dec 2019 10:09:15 +0530 Subject: [PATCH] fixes #323 - Required slice option --- ini.go | 5 ++++- ini_test.go | 25 +++++++++++++++++++++++++ option.go | 6 +++++- parser.go | 6 +++++- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/ini.go b/ini.go index e714d3d..3953ee8 100644 --- a/ini.go +++ b/ini.go @@ -498,7 +498,9 @@ func (i *IniParser) matchingGroups(name string) []*Group { func (i *IniParser) parse(ini *ini) error { p := i.parser - + p.eachOption(func(c *Command, g *Group, o *Option) { + o.isProcessingIni = true + }) var quotesLookup = make(map[*Option]bool) for name, section := range ini.Sections { @@ -579,6 +581,7 @@ func (i *IniParser) parse(ini *ini) error { LineNumber: inival.LineNumber, } } + opt.setFromIni = true // either all INI values are quoted or only values who need quoting if _, ok := quotesLookup[opt]; !inival.Quoted || !ok { diff --git a/ini_test.go b/ini_test.go index ad4852e..19bf407 100644 --- a/ini_test.go +++ b/ini_test.go @@ -257,6 +257,31 @@ EnvDefault2 = env-def } } +func TestIniRequiredSlice_ShouldNotNeedToBeSpecifiedOnCli(t *testing.T) { + type options struct { + Items []string `long:"item" required:"true"` + } + var opts options + ini := ` +[Application Options] +item=abc` + args := []string{} + + parser := NewParser(&opts, Default) + inip := NewIniParser(parser) + + inip.Parse(strings.NewReader(ini)) + inip.ParseAsDefaults = false + _, err := parser.ParseArgs(args) + if err != nil { + t.Fatalf("Unexpected failure: %v", err) + } + if opts.Items[0] != "abc" { + t.Fatalf("Expected first option to be abc, but was %v", opts.Items[0]) + } + +} + func TestReadIni_flagEquivalent(t *testing.T) { type options struct { Opt1 bool `long:"opt1"` diff --git a/option.go b/option.go index 5cebb54..6aa992a 100644 --- a/option.go +++ b/option.go @@ -86,6 +86,9 @@ type Option struct { preventDefault bool defaultLiteral string + + isProcessingIni bool + setFromIni bool } // LongNameWithNamespace returns the option's long name with the group namespaces @@ -241,8 +244,9 @@ func (option *Option) IsSetDefault() bool { func (option *Option) set(value *string) error { kind := option.value.Type().Kind() - if (kind == reflect.Map || kind == reflect.Slice) && !option.isSet { + if (kind == reflect.Map || kind == reflect.Slice) && (!option.isSet || (!option.isProcessingIni && option.setFromIni)) { option.empty() + option.setFromIni = false } option.isSet = true diff --git a/parser.go b/parser.go index a5347b0..248487a 100644 --- a/parser.go +++ b/parser.go @@ -6,6 +6,7 @@ package flags import ( "bytes" + "reflect" "fmt" "os" "path" @@ -207,7 +208,10 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) { } p.eachOption(func(c *Command, g *Group, option *Option) { - option.isSet = false + option.isProcessingIni = false + if !(option.value.Kind() == reflect.Slice || option.value.Kind() == reflect.Map) { + option.isSet = false + } option.isSetDefault = false option.updateDefaultLiteral() })