Skip to content
Merged
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
2 changes: 1 addition & 1 deletion flag_bool_with_inverse.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (parent *BoolWithInverseFlag) initialize() {
sources := []ValueSource{}

for _, envVar := range child.GetEnvVars() {
sources = append(sources, &envVarValueSource{Key: strings.ToUpper(parent.InversePrefix) + envVar})
sources = append(sources, EnvVar(strings.ToUpper(parent.InversePrefix)+envVar))
}
parent.negativeFlag.Sources = NewValueSourceChain(sources...)
}
Expand Down
22 changes: 22 additions & 0 deletions godoc-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,12 @@ type DocGenerationMultiValueFlag interface {

type DurationFlag = FlagBase[time.Duration, NoConfig, durationValue]

type EnvValueSource interface {
IsFromEnv() bool
Key() string
}
EnvValueSource is to specifically detect env sources when printing help text

type ErrorFormatter interface {
Format(s fmt.State, verb rune)
}
Expand Down Expand Up @@ -848,6 +854,20 @@ func (i MapBase[T, C, VC]) ToString(t map[string]T) string
func (i *MapBase[T, C, VC]) Value() map[string]T
Value returns the mapping of values set by this flag

type MapSource interface {
fmt.Stringer
fmt.GoStringer

// Lookup returns the value from the source based on key
// and if it was found
// or returns an empty string and false
Lookup(string) (any, bool)
}
MapSource is a source which can be used to look up a value based on a key
typically for use with a cli.Flag

func NewMapSource(name string, m map[any]any) MapSource

type MultiError interface {
error
Errors() []error
Expand Down Expand Up @@ -1000,6 +1020,8 @@ func EnvVar(key string) ValueSource

func File(path string) ValueSource

func NewMapValueSource(key string, ms MapSource) ValueSource

type ValueSourceChain struct {
Chain []ValueSource
}
Expand Down
22 changes: 22 additions & 0 deletions testdata/godoc-v3.x.txt
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,12 @@ type DocGenerationMultiValueFlag interface {

type DurationFlag = FlagBase[time.Duration, NoConfig, durationValue]

type EnvValueSource interface {
IsFromEnv() bool
Key() string
}
EnvValueSource is to specifically detect env sources when printing help text

type ErrorFormatter interface {
Format(s fmt.State, verb rune)
}
Expand Down Expand Up @@ -848,6 +854,20 @@ func (i MapBase[T, C, VC]) ToString(t map[string]T) string
func (i *MapBase[T, C, VC]) Value() map[string]T
Value returns the mapping of values set by this flag

type MapSource interface {
fmt.Stringer
fmt.GoStringer

// Lookup returns the value from the source based on key
// and if it was found
// or returns an empty string and false
Lookup(string) (any, bool)
}
MapSource is a source which can be used to look up a value based on a key
typically for use with a cli.Flag

func NewMapSource(name string, m map[any]any) MapSource

type MultiError interface {
error
Errors() []error
Expand Down Expand Up @@ -1000,6 +1020,8 @@ func EnvVar(key string) ValueSource

func File(path string) ValueSource

func NewMapValueSource(key string, ms MapSource) ValueSource

type ValueSourceChain struct {
Chain []ValueSource
}
Expand Down
123 changes: 114 additions & 9 deletions value_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@ type ValueSource interface {
Lookup() (string, bool)
}

// EnvValueSource is to specifically detect env sources when
// printing help text
type EnvValueSource interface {
IsFromEnv() bool
Key() string
}

// MapSource is a source which can be used to look up a value
// based on a key
// typically for use with a cli.Flag
type MapSource interface {
fmt.Stringer
fmt.GoStringer

// Lookup returns the value from the source based on key
// and if it was found
// or returns an empty string and false
Lookup(string) (any, bool)
}

// ValueSourceChain contains an ordered series of ValueSource that
// allows for lookup where the first ValueSource to resolve is
// returned
Expand All @@ -38,8 +58,8 @@ func (vsc *ValueSourceChain) EnvKeys() []string {
vals := []string{}

for _, src := range vsc.Chain {
if v, ok := src.(*envVarValueSource); ok {
vals = append(vals, v.Key)
if v, ok := src.(EnvValueSource); ok && v.IsFromEnv() {
vals = append(vals, v.Key())
}
}

Expand Down Expand Up @@ -83,21 +103,29 @@ func (vsc *ValueSourceChain) LookupWithSource() (string, ValueSource, bool) {

// envVarValueSource encapsulates a ValueSource from an environment variable
type envVarValueSource struct {
Key string
key string
}

func (e *envVarValueSource) Lookup() (string, bool) {
return os.LookupEnv(strings.TrimSpace(string(e.Key)))
return os.LookupEnv(strings.TrimSpace(string(e.key)))
}

func (e *envVarValueSource) String() string { return fmt.Sprintf("environment variable %[1]q", e.Key) }
func (e *envVarValueSource) IsFromEnv() bool {
return true
}

func (e *envVarValueSource) Key() string {
return e.key
}

func (e *envVarValueSource) String() string { return fmt.Sprintf("environment variable %[1]q", e.key) }
func (e *envVarValueSource) GoString() string {
return fmt.Sprintf("&envVarValueSource{Key:%[1]q}", e.Key)
return fmt.Sprintf("&envVarValueSource{Key:%[1]q}", e.key)
}

func EnvVar(key string) ValueSource {
return &envVarValueSource{
Key: key,
key: key,
}
}

Expand All @@ -107,7 +135,7 @@ func EnvVars(keys ...string) ValueSourceChain {
vsc := ValueSourceChain{Chain: []ValueSource{}}

for _, key := range keys {
vsc.Chain = append(vsc.Chain, &envVarValueSource{Key: key})
vsc.Chain = append(vsc.Chain, EnvVar(key))
}

return vsc
Expand Down Expand Up @@ -138,8 +166,85 @@ func Files(paths ...string) ValueSourceChain {
vsc := ValueSourceChain{Chain: []ValueSource{}}

for _, path := range paths {
vsc.Chain = append(vsc.Chain, &fileValueSource{Path: path})
vsc.Chain = append(vsc.Chain, File(path))
}

return vsc
}

type mapSource struct {
name string
m map[any]any
}

func NewMapSource(name string, m map[any]any) MapSource {
return &mapSource{
name: name,
m: m,
}
}

func (ms *mapSource) String() string { return fmt.Sprintf("map source %[1]q", ms.name) }
func (ms *mapSource) GoString() string {
return fmt.Sprintf("&mapSource{name:%[1]q}", ms.name)
}

func (ms *mapSource) Lookup(name string) (any, bool) {
// nestedVal checks if the name has '.' delimiters.
// If so, it tries to traverse the tree by the '.' delimited sections to find
// a nested value for the key.
if sections := strings.Split(name, "."); len(sections) > 1 {
node := ms.m
for _, section := range sections[:len(sections)-1] {
child, ok := node[section]
if !ok {
return nil, false
}

switch child := child.(type) {
case map[string]any:
node = make(map[any]any, len(child))
for k, v := range child {
node[k] = v
}
case map[any]any:
node = child
default:
return nil, false
}
}
if val, ok := node[sections[len(sections)-1]]; ok {
return val, true
}
}

return nil, false
}

type mapValueSource struct {
key string
ms MapSource
}

func NewMapValueSource(key string, ms MapSource) ValueSource {
return &mapValueSource{
key: key,
ms: ms,
}
}

func (mvs *mapValueSource) String() string {
return fmt.Sprintf("key %[1]q from %[2]s", mvs.key, mvs.ms.String())
}

func (mvs *mapValueSource) GoString() string {
return fmt.Sprintf("&mapValueSource{key:%[1]q, src:%[2]s}", mvs.key, mvs.ms.GoString())
}

func (mvs *mapValueSource) Lookup() (string, bool) {
if v, ok := mvs.ms.Lookup(mvs.key); !ok {
return "", false
} else {
return fmt.Sprintf("%+v", v), true
}
}
Loading
Loading