Skip to content

Commit 0a1c772

Browse files
authored
Merge pull request #2009 from dearchap/add_map_value_source
Add map value source
2 parents ef45965 + 6c78afd commit 0a1c772

5 files changed

+298
-10
lines changed

flag_bool_with_inverse.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (parent *BoolWithInverseFlag) initialize() {
106106
sources := []ValueSource{}
107107

108108
for _, envVar := range child.GetEnvVars() {
109-
sources = append(sources, &envVarValueSource{Key: strings.ToUpper(parent.InversePrefix) + envVar})
109+
sources = append(sources, EnvVar(strings.ToUpper(parent.InversePrefix)+envVar))
110110
}
111111
parent.negativeFlag.Sources = NewValueSourceChain(sources...)
112112
}

godoc-current.txt

+22
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,12 @@ type DocGenerationMultiValueFlag interface {
591591

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

594+
type EnvValueSource interface {
595+
IsFromEnv() bool
596+
Key() string
597+
}
598+
EnvValueSource is to specifically detect env sources when printing help text
599+
594600
type ErrorFormatter interface {
595601
Format(s fmt.State, verb rune)
596602
}
@@ -848,6 +854,20 @@ func (i MapBase[T, C, VC]) ToString(t map[string]T) string
848854
func (i *MapBase[T, C, VC]) Value() map[string]T
849855
Value returns the mapping of values set by this flag
850856

857+
type MapSource interface {
858+
fmt.Stringer
859+
fmt.GoStringer
860+
861+
// Lookup returns the value from the source based on key
862+
// and if it was found
863+
// or returns an empty string and false
864+
Lookup(string) (any, bool)
865+
}
866+
MapSource is a source which can be used to look up a value based on a key
867+
typically for use with a cli.Flag
868+
869+
func NewMapSource(name string, m map[any]any) MapSource
870+
851871
type MultiError interface {
852872
error
853873
Errors() []error
@@ -1000,6 +1020,8 @@ func EnvVar(key string) ValueSource
10001020

10011021
func File(path string) ValueSource
10021022

1023+
func NewMapValueSource(key string, ms MapSource) ValueSource
1024+
10031025
type ValueSourceChain struct {
10041026
Chain []ValueSource
10051027
}

testdata/godoc-v3.x.txt

+22
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,12 @@ type DocGenerationMultiValueFlag interface {
591591

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

594+
type EnvValueSource interface {
595+
IsFromEnv() bool
596+
Key() string
597+
}
598+
EnvValueSource is to specifically detect env sources when printing help text
599+
594600
type ErrorFormatter interface {
595601
Format(s fmt.State, verb rune)
596602
}
@@ -848,6 +854,20 @@ func (i MapBase[T, C, VC]) ToString(t map[string]T) string
848854
func (i *MapBase[T, C, VC]) Value() map[string]T
849855
Value returns the mapping of values set by this flag
850856

857+
type MapSource interface {
858+
fmt.Stringer
859+
fmt.GoStringer
860+
861+
// Lookup returns the value from the source based on key
862+
// and if it was found
863+
// or returns an empty string and false
864+
Lookup(string) (any, bool)
865+
}
866+
MapSource is a source which can be used to look up a value based on a key
867+
typically for use with a cli.Flag
868+
869+
func NewMapSource(name string, m map[any]any) MapSource
870+
851871
type MultiError interface {
852872
error
853873
Errors() []error
@@ -1000,6 +1020,8 @@ func EnvVar(key string) ValueSource
10001020

10011021
func File(path string) ValueSource
10021022

1023+
func NewMapValueSource(key string, ms MapSource) ValueSource
1024+
10031025
type ValueSourceChain struct {
10041026
Chain []ValueSource
10051027
}

value_source.go

+114-9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,26 @@ type ValueSource interface {
1717
Lookup() (string, bool)
1818
}
1919

20+
// EnvValueSource is to specifically detect env sources when
21+
// printing help text
22+
type EnvValueSource interface {
23+
IsFromEnv() bool
24+
Key() string
25+
}
26+
27+
// MapSource is a source which can be used to look up a value
28+
// based on a key
29+
// typically for use with a cli.Flag
30+
type MapSource interface {
31+
fmt.Stringer
32+
fmt.GoStringer
33+
34+
// Lookup returns the value from the source based on key
35+
// and if it was found
36+
// or returns an empty string and false
37+
Lookup(string) (any, bool)
38+
}
39+
2040
// ValueSourceChain contains an ordered series of ValueSource that
2141
// allows for lookup where the first ValueSource to resolve is
2242
// returned
@@ -38,8 +58,8 @@ func (vsc *ValueSourceChain) EnvKeys() []string {
3858
vals := []string{}
3959

4060
for _, src := range vsc.Chain {
41-
if v, ok := src.(*envVarValueSource); ok {
42-
vals = append(vals, v.Key)
61+
if v, ok := src.(EnvValueSource); ok && v.IsFromEnv() {
62+
vals = append(vals, v.Key())
4363
}
4464
}
4565

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

84104
// envVarValueSource encapsulates a ValueSource from an environment variable
85105
type envVarValueSource struct {
86-
Key string
106+
key string
87107
}
88108

89109
func (e *envVarValueSource) Lookup() (string, bool) {
90-
return os.LookupEnv(strings.TrimSpace(string(e.Key)))
110+
return os.LookupEnv(strings.TrimSpace(string(e.key)))
91111
}
92112

93-
func (e *envVarValueSource) String() string { return fmt.Sprintf("environment variable %[1]q", e.Key) }
113+
func (e *envVarValueSource) IsFromEnv() bool {
114+
return true
115+
}
116+
117+
func (e *envVarValueSource) Key() string {
118+
return e.key
119+
}
120+
121+
func (e *envVarValueSource) String() string { return fmt.Sprintf("environment variable %[1]q", e.key) }
94122
func (e *envVarValueSource) GoString() string {
95-
return fmt.Sprintf("&envVarValueSource{Key:%[1]q}", e.Key)
123+
return fmt.Sprintf("&envVarValueSource{Key:%[1]q}", e.key)
96124
}
97125

98126
func EnvVar(key string) ValueSource {
99127
return &envVarValueSource{
100-
Key: key,
128+
key: key,
101129
}
102130
}
103131

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

109137
for _, key := range keys {
110-
vsc.Chain = append(vsc.Chain, &envVarValueSource{Key: key})
138+
vsc.Chain = append(vsc.Chain, EnvVar(key))
111139
}
112140

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

140168
for _, path := range paths {
141-
vsc.Chain = append(vsc.Chain, &fileValueSource{Path: path})
169+
vsc.Chain = append(vsc.Chain, File(path))
142170
}
143171

144172
return vsc
145173
}
174+
175+
type mapSource struct {
176+
name string
177+
m map[any]any
178+
}
179+
180+
func NewMapSource(name string, m map[any]any) MapSource {
181+
return &mapSource{
182+
name: name,
183+
m: m,
184+
}
185+
}
186+
187+
func (ms *mapSource) String() string { return fmt.Sprintf("map source %[1]q", ms.name) }
188+
func (ms *mapSource) GoString() string {
189+
return fmt.Sprintf("&mapSource{name:%[1]q}", ms.name)
190+
}
191+
192+
func (ms *mapSource) Lookup(name string) (any, bool) {
193+
// nestedVal checks if the name has '.' delimiters.
194+
// If so, it tries to traverse the tree by the '.' delimited sections to find
195+
// a nested value for the key.
196+
if sections := strings.Split(name, "."); len(sections) > 1 {
197+
node := ms.m
198+
for _, section := range sections[:len(sections)-1] {
199+
child, ok := node[section]
200+
if !ok {
201+
return nil, false
202+
}
203+
204+
switch child := child.(type) {
205+
case map[string]any:
206+
node = make(map[any]any, len(child))
207+
for k, v := range child {
208+
node[k] = v
209+
}
210+
case map[any]any:
211+
node = child
212+
default:
213+
return nil, false
214+
}
215+
}
216+
if val, ok := node[sections[len(sections)-1]]; ok {
217+
return val, true
218+
}
219+
}
220+
221+
return nil, false
222+
}
223+
224+
type mapValueSource struct {
225+
key string
226+
ms MapSource
227+
}
228+
229+
func NewMapValueSource(key string, ms MapSource) ValueSource {
230+
return &mapValueSource{
231+
key: key,
232+
ms: ms,
233+
}
234+
}
235+
236+
func (mvs *mapValueSource) String() string {
237+
return fmt.Sprintf("key %[1]q from %[2]s", mvs.key, mvs.ms.String())
238+
}
239+
240+
func (mvs *mapValueSource) GoString() string {
241+
return fmt.Sprintf("&mapValueSource{key:%[1]q, src:%[2]s}", mvs.key, mvs.ms.GoString())
242+
}
243+
244+
func (mvs *mapValueSource) Lookup() (string, bool) {
245+
if v, ok := mvs.ms.Lookup(mvs.key); !ok {
246+
return "", false
247+
} else {
248+
return fmt.Sprintf("%+v", v), true
249+
}
250+
}

0 commit comments

Comments
 (0)