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
16 changes: 13 additions & 3 deletions flag_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,22 @@ func (f *FlagBase[T, C, V]) PostParse() error {

if !f.hasBeenSet {
if val, source, found := f.Sources.LookupWithSource(); found {
if val != "" || reflect.TypeOf(f.Value).Kind() == reflect.String {
// reflect.TypeOf yields nil when T is an interface type (e.g.
// GenericFlag) and the value is nil, so the kind has to be
// derived defensively.
kind := reflect.Invalid
if ty := reflect.TypeOf(f.Value); ty != nil {
kind = ty.Kind()
}

if val != "" || kind == reflect.String {
if err := f.Set(f.Name, val); err != nil {
return fmt.Errorf(
"could not parse %[1]q as %[2]T value from %[3]s for flag %[4]s: %[5]s",
val, f.Value, source, f.Name, err,
)
}
} else if val == "" && reflect.TypeOf(f.Value).Kind() == reflect.Bool {
} else if val == "" && kind == reflect.Bool {
_ = f.Set(f.Name, "false")
}

Expand Down Expand Up @@ -316,8 +324,10 @@ func (f *FlagBase[T, C, V]) SchemaType() string {
// SchemaItemsType returns the JSON Schema element type for slice flags.
func (f *FlagBase[T, C, V]) SchemaItemsType() string {
var zero T
// reflect.TypeOf yields nil when T is an interface type (e.g. GenericFlag),
// in which case there are no slice elements to describe.
t := reflect.TypeOf(zero)
if t.Kind() == reflect.Slice {
if t != nil && t.Kind() == reflect.Slice {
switch t.Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
Expand Down
27 changes: 27 additions & 0 deletions flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3518,3 +3518,30 @@ func TestEndValue(t *testing.T) {
assert.Error(t, cmd.Run(buildTestContext(t), []string{"foo", "-cd="}))
assert.Error(t, cmd.Run(buildTestContext(t), []string{"foo", "-cd=s"}))
}

// TestFlagBaseInterfaceValueType covers flags whose value type is an interface
// (GenericFlag), for which reflect.TypeOf on the zero value yields nil.
func TestFlagBaseInterfaceValueType(t *testing.T) {
t.Run("SchemaItemsType", func(t *testing.T) {
fl := &GenericFlag{Name: "gen"}
assert.Empty(t, fl.SchemaItemsType())
})

t.Run("PostParse with empty source value", func(t *testing.T) {
t.Setenv("TEST_GENERIC_EMPTY", "")

fl := &GenericFlag{Name: "gen", Sources: EnvVars("TEST_GENERIC_EMPTY")}
require.NoError(t, fl.PreParse())
require.NoError(t, fl.PostParse())
})

t.Run("PostParse with non-empty source value", func(t *testing.T) {
t.Setenv("TEST_GENERIC_SET", "abc,def")

val := &Parser{}
fl := &GenericFlag{Name: "gen", Value: val, Sources: EnvVars("TEST_GENERIC_SET")}
require.NoError(t, fl.PreParse())
require.NoError(t, fl.PostParse())
assert.True(t, fl.IsSet())
})
}