1
// Copyright © 2014 Steve Francia <spf@spf13.com>.
3
// Use of this source code is governed by an MIT-style
4
// license that can be found in the LICENSE file.
6
// Viper is a application configuration system.
7
// It believes that applications can be configured a variety of ways
8
// via flags, ENVIRONMENT variables, configuration files retrieved
9
// from the file system, or a remote key/value store.
21
"github.com/spf13/cast"
24
// ConfigParseError denotes failing to parse configuration file.
25
type ConfigParseError struct {
29
// Error returns the formatted configuration error.
30
func (pe ConfigParseError) Error() string {
31
return fmt.Sprintf("While parsing config: %s", pe.err.Error())
34
// Unwrap returns the wrapped error.
35
func (pe ConfigParseError) Unwrap() error {
39
// toCaseInsensitiveValue checks if the value is a map;
40
// if so, create a copy and lower-case the keys recursively.
41
func toCaseInsensitiveValue(value interface{}) interface{} {
42
switch v := value.(type) {
43
case map[interface{}]interface{}:
44
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
45
case map[string]interface{}:
46
value = copyAndInsensitiviseMap(v)
52
// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
53
// any map it makes case insensitive.
54
func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
55
nm := make(map[string]interface{})
57
for key, val := range m {
58
lkey := strings.ToLower(key)
59
switch v := val.(type) {
60
case map[interface{}]interface{}:
61
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
62
case map[string]interface{}:
63
nm[lkey] = copyAndInsensitiviseMap(v)
72
func insensitiviseVal(val interface{}) interface{} {
74
case map[interface{}]interface{}:
75
// nested map: cast and recursively insensitivise
76
val = cast.ToStringMap(val)
77
insensitiviseMap(val.(map[string]interface{}))
78
case map[string]interface{}:
79
// nested map: recursively insensitivise
80
insensitiviseMap(val.(map[string]interface{}))
82
// nested array: recursively insensitivise
83
insensitiveArray(val.([]interface{}))
88
func insensitiviseMap(m map[string]interface{}) {
89
for key, val := range m {
90
val = insensitiviseVal(val)
91
lower := strings.ToLower(key)
93
// remove old key (not lower-cased)
101
func insensitiveArray(a []interface{}) {
102
for i, val := range a {
103
a[i] = insensitiviseVal(val)
107
func absPathify(logger Logger, inPath string) string {
108
logger.Info("trying to resolve absolute path", "path", inPath)
110
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
111
inPath = userHomeDir() + inPath[5:]
114
inPath = os.ExpandEnv(inPath)
116
if filepath.IsAbs(inPath) {
117
return filepath.Clean(inPath)
120
p, err := filepath.Abs(inPath)
122
return filepath.Clean(p)
125
logger.Error(fmt.Errorf("could not discover absolute path: %w", err).Error())
130
func stringInSlice(a string, list []string) bool {
131
for _, b := range list {
139
func userHomeDir() string {
140
if runtime.GOOS == "windows" {
141
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
143
home = os.Getenv("USERPROFILE")
147
return os.Getenv("HOME")
150
func safeMul(a, b uint) uint {
152
if a > 1 && b > 1 && c/b != a {
158
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
159
func parseSizeInBytes(sizeStr string) uint {
160
sizeStr = strings.TrimSpace(sizeStr)
161
lastChar := len(sizeStr) - 1
162
multiplier := uint(1)
165
if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
167
switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
170
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
173
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
176
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
179
sizeStr = strings.TrimSpace(sizeStr[:lastChar])
185
size := cast.ToInt(sizeStr)
190
return safeMul(uint(size), multiplier)
193
// deepSearch scans deep maps, following the key indexes listed in the
195
// The last value is expected to be another map, and is returned.
197
// In case intermediate keys do not exist, or map to a non-map value,
198
// a new map is created and inserted, and the search continues from there:
199
// the initial map "m" may be modified!
200
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
201
for _, k := range path {
204
// intermediate key does not exist
205
// => create it and continue from there
206
m3 := make(map[string]interface{})
211
m3, ok := m2.(map[string]interface{})
213
// intermediate key is a value
214
// => replace with a new map
215
m3 = make(map[string]interface{})
218
// continue search from here