-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloader_decorator_flatten.go
133 lines (116 loc) · 3.68 KB
/
loader_decorator_flatten.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright The ActForGood Authors.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://github.com/actforgood/xconf/blob/main/LICENSE.
package xconf
import "github.com/spf13/cast"
// FlattenLoader decorates another loader to add shortcuts to leaves' information
// in a nested configuration key.
//
// Example, given the configuration:
//
// {
// "mysql": {
// "host": "127.0.0.1",
// "port": 3306
// }
// }
//
// 2 additional flat keys will be added to above standard configuration: "mysql.host", "mysql.port"
// for easy access of leaf-keys.
// Note: original nested configuration is still kept by default, if you want to remove it, apply
// [FlattenLoaderWithFlatKeysOnly] option.
type FlattenLoader struct {
// original, decorated loader.
loader Loader
// flag that indicates whether nested keys should be removed and only their flat version should be kept.
flatOnly bool
// separator for flat nested keys.
separator string
}
// NewFlattenLoader instantiates a new FlattenLoader object that adds
// flat version for nested keys for easily access.
func NewFlattenLoader(loader Loader, opts ...FlattenLoaderOption) FlattenLoader {
flattenLoader := FlattenLoader{
loader: loader,
flatOnly: false,
separator: ".",
}
// apply options, if any.
for _, opt := range opts {
opt(&flattenLoader)
}
return flattenLoader
}
// Load returns a configuration key-value map from original loader, enriched with
// shortcuts to leaves' information in nested configuration key(s).
func (decorator FlattenLoader) Load() (map[string]any, error) {
configMap, err := decorator.loader.Load()
if err != nil {
return configMap, err
}
flatConfigMap := configMap
decorator.flattenConfigMap(0, "", configMap, flatConfigMap)
return flatConfigMap, nil
}
// getFlatKey returns a flat key representing the concatenation of
// previous (level) key and current (level) key.
func (decorator FlattenLoader) getFlatKey(lvl uint, prevKey, currKey string) string {
if lvl > 0 {
return prevKey + decorator.separator + currKey
}
return currKey
}
// flattenConfigMap appends flat keys to finalConfigMap,
// and eventually removes nested keys from it.
func (decorator FlattenLoader) flattenConfigMap(
lvl uint,
prevKey string,
currConfigMap map[string]any,
finalConfigMap map[string]any,
) {
for key, value := range currConfigMap {
switch val := value.(type) {
case map[string]any:
decorator.flattenConfigMap(
lvl+1,
decorator.getFlatKey(lvl, prevKey, key),
val,
finalConfigMap,
)
if lvl == 0 && decorator.flatOnly {
delete(finalConfigMap, key) // don't preserve original (nested configuration) keys
}
case map[any]any:
cfgMap := cast.ToStringMap(val)
decorator.flattenConfigMap(
lvl+1,
decorator.getFlatKey(lvl, prevKey, key),
cfgMap,
finalConfigMap,
)
if lvl == 0 && decorator.flatOnly {
delete(finalConfigMap, key) // don't preserve original (nested configuration) keys
}
default:
finalConfigMap[decorator.getFlatKey(lvl, prevKey, key)] = value
}
}
}
// FlattenLoaderOption defines optional function for configuring
// a Flatten Loader.
type FlattenLoaderOption func(*FlattenLoader)
// FlattenLoaderWithSeparator sets the separator for the new, flat keys.
// By default, is set to "."(dot).
func FlattenLoaderWithSeparator(keySeparator string) FlattenLoaderOption {
return func(loader *FlattenLoader) {
loader.separator = keySeparator
}
}
// FlattenLoaderWithFlatKeysOnly triggers nested keys to be removed,
// and only their flat version to be kept.
func FlattenLoaderWithFlatKeysOnly() FlattenLoaderOption {
return func(loader *FlattenLoader) {
loader.flatOnly = true
}
}