From 9d14bb592a9773a6fc9fcef31e2fa2108fd7dded Mon Sep 17 00:00:00 2001 From: Peter Ebden Date: Fri, 6 Jun 2025 08:56:10 +0100 Subject: [PATCH 1/3] Add Defaulter interface for types that can dynamically provide a default value --- convert.go | 6 ++++++ group.go | 4 ++++ parser_test.go | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/convert.go b/convert.go index b27f698d..76ec5d58 100644 --- a/convert.go +++ b/convert.go @@ -37,6 +37,12 @@ type ValueValidator interface { IsValidValue(value string) error } +// Defaulter is the interface implemented by types that can provide a dynamic +// default value at runtime. +type Defaulter interface { + Default() []string +} + func getBase(options multiTag, base int) (int, error) { sbase := options.Get("base") diff --git a/group.go b/group.go index 181caabb..98b34773 100644 --- a/group.go +++ b/group.go @@ -313,6 +313,10 @@ func (g *Group) scanStruct(realval reflect.Value, sfield *reflect.StructField, h option.shortAndLongName()) } + if defaulter, ok := option.value.Interface().(Defaulter); ok { + option.Default = defaulter.Default() + } + g.options = append(g.options, option) } diff --git a/parser_test.go b/parser_test.go index e675cfd2..808481ec 100644 --- a/parser_test.go +++ b/parser_test.go @@ -168,6 +168,25 @@ func TestNoDefaultsForBools(t *testing.T) { } } +func TestDynamicDefaults(t *testing.T) { + var opts struct { + DD DynamicDefault `short:"d"` + } + _, err := ParseArgs(&opts, []string{"test"}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if opts.DD != 42 { + t.Errorf("Dynamic default value not provided; expected 42 but was %d", opts.DD) + } +} + +type DynamicDefault int + +func (dd DynamicDefault) Default() []string { + return []string{"42"} +} + func TestUnquoting(t *testing.T) { var tests = []struct { arg string From 4b4a077d9bc03e138baa207d2fafac0af4dc37e9 Mon Sep 17 00:00:00 2001 From: Peter Ebden Date: Fri, 6 Jun 2025 09:01:15 +0100 Subject: [PATCH 2/3] Allow it to return an error --- convert.go | 2 +- group.go | 6 +++++- parser_test.go | 24 ++++++++++++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/convert.go b/convert.go index 76ec5d58..cb24268d 100644 --- a/convert.go +++ b/convert.go @@ -40,7 +40,7 @@ type ValueValidator interface { // Defaulter is the interface implemented by types that can provide a dynamic // default value at runtime. type Defaulter interface { - Default() []string + Default() ([]string, error) } func getBase(options multiTag, base int) (int, error) { diff --git a/group.go b/group.go index 98b34773..a2d992ea 100644 --- a/group.go +++ b/group.go @@ -314,7 +314,11 @@ func (g *Group) scanStruct(realval reflect.Value, sfield *reflect.StructField, h } if defaulter, ok := option.value.Interface().(Defaulter); ok { - option.Default = defaulter.Default() + def, err := defaulter.Default() + if err != nil { + return err + } + option.Default = def } g.options = append(g.options, option) diff --git a/parser_test.go b/parser_test.go index 808481ec..95f5db8d 100644 --- a/parser_test.go +++ b/parser_test.go @@ -181,10 +181,30 @@ func TestDynamicDefaults(t *testing.T) { } } +func TestDynamicDefaultError(t *testing.T) { + var opts struct { + DD DynamicDefaultError `short:"d"` + } + _, err := ParseArgs(&opts, []string{"test"}) + if err == nil { + t.Fatalf("Expected error, was none") + } + const expected = "couldn't provide default" + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected error %q to contain substring %q", err, expected) + } +} + type DynamicDefault int -func (dd DynamicDefault) Default() []string { - return []string{"42"} +func (dd DynamicDefault) Default() ([]string, error) { + return []string{"42"}, nil +} + +type DynamicDefaultError int + +func (dd DynamicDefaultError) Default() ([]string, error) { + return nil, errors.New("couldn't provide default") } func TestUnquoting(t *testing.T) { From d3cfb48ce721ef4630c46852e12ed438dc13c796 Mon Sep 17 00:00:00 2001 From: Peter Ebden Date: Fri, 6 Jun 2025 09:08:11 +0100 Subject: [PATCH 3/3] pluralise --- convert.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/convert.go b/convert.go index cb24268d..6c696fe8 100644 --- a/convert.go +++ b/convert.go @@ -37,8 +37,8 @@ type ValueValidator interface { IsValidValue(value string) error } -// Defaulter is the interface implemented by types that can provide a dynamic -// default value at runtime. +// Defaulter is the interface implemented by types that can provide dynamic +// default values at runtime. type Defaulter interface { Default() ([]string, error) }