@@ -17,6 +17,26 @@ type ValueSource interface {
17
17
Lookup () (string , bool )
18
18
}
19
19
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
+
20
40
// ValueSourceChain contains an ordered series of ValueSource that
21
41
// allows for lookup where the first ValueSource to resolve is
22
42
// returned
@@ -38,8 +58,8 @@ func (vsc *ValueSourceChain) EnvKeys() []string {
38
58
vals := []string {}
39
59
40
60
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 () )
43
63
}
44
64
}
45
65
@@ -83,21 +103,29 @@ func (vsc *ValueSourceChain) LookupWithSource() (string, ValueSource, bool) {
83
103
84
104
// envVarValueSource encapsulates a ValueSource from an environment variable
85
105
type envVarValueSource struct {
86
- Key string
106
+ key string
87
107
}
88
108
89
109
func (e * envVarValueSource ) Lookup () (string , bool ) {
90
- return os .LookupEnv (strings .TrimSpace (string (e .Key )))
110
+ return os .LookupEnv (strings .TrimSpace (string (e .key )))
91
111
}
92
112
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 ) }
94
122
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 )
96
124
}
97
125
98
126
func EnvVar (key string ) ValueSource {
99
127
return & envVarValueSource {
100
- Key : key ,
128
+ key : key ,
101
129
}
102
130
}
103
131
@@ -107,7 +135,7 @@ func EnvVars(keys ...string) ValueSourceChain {
107
135
vsc := ValueSourceChain {Chain : []ValueSource {}}
108
136
109
137
for _ , key := range keys {
110
- vsc .Chain = append (vsc .Chain , & envVarValueSource { Key : key } )
138
+ vsc .Chain = append (vsc .Chain , EnvVar ( key ) )
111
139
}
112
140
113
141
return vsc
@@ -138,8 +166,85 @@ func Files(paths ...string) ValueSourceChain {
138
166
vsc := ValueSourceChain {Chain : []ValueSource {}}
139
167
140
168
for _ , path := range paths {
141
- vsc .Chain = append (vsc .Chain , & fileValueSource { Path : path } )
169
+ vsc .Chain = append (vsc .Chain , File ( path ) )
142
170
}
143
171
144
172
return vsc
145
173
}
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