@@ -12,6 +12,8 @@ import (
12
12
13
13
"github.com/FZambia/viper-lite"
14
14
"github.com/google/uuid"
15
+ "github.com/mitchellh/mapstructure"
16
+ "github.com/rs/zerolog/log"
15
17
)
16
18
17
19
// pathExists returns whether the given file or directory exists or not
@@ -168,3 +170,67 @@ func OptionalStringChoice(v *viper.Viper, key string, choices []string) (string,
168
170
}
169
171
return val , nil
170
172
}
173
+
174
+ // DecoderConfig returns default mapstructure.DecoderConfig with support
175
+ // of time.Duration values & string slices & Duration
176
+ func DecoderConfig (output any ) * mapstructure.DecoderConfig {
177
+ return & mapstructure.DecoderConfig {
178
+ Metadata : nil ,
179
+ Result : output ,
180
+ WeaklyTypedInput : true ,
181
+ DecodeHook : mapstructure .ComposeDecodeHookFunc (
182
+ StringToDurationHookFunc (),
183
+ mapstructure .StringToTimeDurationHookFunc (),
184
+ mapstructure .StringToSliceHookFunc ("," ),
185
+ ),
186
+ }
187
+ }
188
+
189
+ func DecodeSlice (v * viper.Viper , dst any , key string ) []byte {
190
+ var jsonData []byte
191
+ var err error
192
+ switch val := v .Get (key ).(type ) {
193
+ case string :
194
+ jsonData = []byte (val )
195
+ err = json .Unmarshal ([]byte (val ), dst )
196
+ case []any :
197
+ jsonData , err = json .Marshal (translateMap (val ))
198
+ if err != nil {
199
+ log .Fatal ().Err (err ).Msgf ("error marshalling config %s slice" , key )
200
+ }
201
+ decoderCfg := DecoderConfig (dst )
202
+ decoder , newErr := mapstructure .NewDecoder (decoderCfg )
203
+ if newErr != nil {
204
+ log .Fatal ().Msg (newErr .Error ())
205
+ }
206
+ err = decoder .Decode (v .Get (key ))
207
+ default :
208
+ err = fmt .Errorf ("unknown %s type: %T" , key , val )
209
+ }
210
+ if err != nil {
211
+ log .Fatal ().Err (err ).Msgf ("malformed %s" , key )
212
+ }
213
+ return jsonData
214
+ }
215
+
216
+ // translateMap is a helper to deal with map[any]any which YAML uses when unmarshalling.
217
+ // We always use string keys and not making this transform results into errors on JSON marshaling.
218
+ func translateMap (input []any ) []map [string ]any {
219
+ var result []map [string ]any
220
+ for _ , elem := range input {
221
+ switch v := elem .(type ) {
222
+ case map [any ]any :
223
+ translatedMap := make (map [string ]any )
224
+ for key , value := range v {
225
+ stringKey := fmt .Sprintf ("%v" , key )
226
+ translatedMap [stringKey ] = value
227
+ }
228
+ result = append (result , translatedMap )
229
+ case map [string ]any :
230
+ result = append (result , v )
231
+ default :
232
+ log .Fatal ().Msgf ("invalid type in slice: %T" , elem )
233
+ }
234
+ }
235
+ return result
236
+ }
0 commit comments