github.com/hidevopsio/viper@v1.2.2-0.20210220025633-ccb4b202d169/util.go (about) 1 // Copyright © 2014 Steve Francia <spf@spf13.com>. 2 // 3 // Use of this source code is governed by an MIT-style 4 // license that can be found in the LICENSE file. 5 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. 10 11 package viper 12 13 import ( 14 "fmt" 15 "os" 16 "path/filepath" 17 "runtime" 18 "strings" 19 "unicode" 20 21 "github.com/spf13/afero" 22 "github.com/spf13/cast" 23 jww "github.com/spf13/jwalterweatherman" 24 ) 25 26 // ConfigParseError denotes failing to parse configuration file. 27 type ConfigParseError struct { 28 err error 29 } 30 31 // Error returns the formatted configuration error. 32 func (pe ConfigParseError) Error() string { 33 return fmt.Sprintf("While parsing config: %s", pe.err.Error()) 34 } 35 36 // toCaseInsensitiveValue checks if the value is a map; 37 // if so, create a copy and lower-case the keys recursively. 38 func toCaseInsensitiveValue(value interface{}) interface{} { 39 switch v := value.(type) { 40 case map[interface{}]interface{}: 41 value = copyAndInsensitiviseMap(cast.ToStringMap(v)) 42 case map[string]interface{}: 43 value = copyAndInsensitiviseMap(v) 44 } 45 46 return value 47 } 48 49 // copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of 50 // any map it makes case insensitive. 51 func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} { 52 nm := make(map[string]interface{}) 53 54 for key, val := range m { 55 lkey := strings.ToLower(key) 56 switch v := val.(type) { 57 case map[interface{}]interface{}: 58 nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v)) 59 case map[string]interface{}: 60 nm[lkey] = copyAndInsensitiviseMap(v) 61 default: 62 nm[lkey] = v 63 } 64 } 65 66 return nm 67 } 68 69 func insensitiviseMap(m map[string]interface{}) { 70 for key, val := range m { 71 switch val.(type) { 72 case map[interface{}]interface{}: 73 // nested map: cast and recursively insensitivise 74 val = cast.ToStringMap(val) 75 insensitiviseMap(val.(map[string]interface{})) 76 case map[string]interface{}: 77 // nested map: recursively insensitivise 78 insensitiviseMap(val.(map[string]interface{})) 79 } 80 81 lower := strings.ToLower(key) 82 if key != lower { 83 // remove old key (not lower-cased) 84 delete(m, key) 85 } 86 // update map 87 m[lower] = val 88 } 89 } 90 91 func absPathify(inPath string) string { 92 jww.INFO.Println("Trying to resolve absolute path to", inPath) 93 94 if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) { 95 inPath = userHomeDir() + inPath[5:] 96 } 97 98 if strings.HasPrefix(inPath, "$") { 99 end := strings.Index(inPath, string(os.PathSeparator)) 100 101 var value, suffix string 102 if end == -1 { 103 value = os.Getenv(inPath[1:]) 104 } else { 105 value = os.Getenv(inPath[1:end]) 106 suffix = inPath[end:] 107 } 108 109 inPath = value + suffix 110 } 111 112 if filepath.IsAbs(inPath) { 113 return filepath.Clean(inPath) 114 } 115 116 p, err := filepath.Abs(inPath) 117 if err == nil { 118 return filepath.Clean(p) 119 } 120 121 jww.ERROR.Println("Couldn't discover absolute path") 122 jww.ERROR.Println(err) 123 return "" 124 } 125 126 // Check if file Exists 127 func exists(fs afero.Fs, path string) (bool, error) { 128 stat, err := fs.Stat(path) 129 if err == nil { 130 return !stat.IsDir(), nil 131 } 132 if os.IsNotExist(err) { 133 return false, nil 134 } 135 return false, err 136 } 137 138 func stringInSlice(a string, list []string) bool { 139 for _, b := range list { 140 if b == a { 141 return true 142 } 143 } 144 return false 145 } 146 147 func userHomeDir() string { 148 if runtime.GOOS == "windows" { 149 home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 150 if home == "" { 151 home = os.Getenv("USERPROFILE") 152 } 153 return home 154 } 155 return os.Getenv("HOME") 156 } 157 158 func safeMul(a, b uint) uint { 159 c := a * b 160 if a > 1 && b > 1 && c/b != a { 161 return 0 162 } 163 return c 164 } 165 166 // parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes 167 func parseSizeInBytes(sizeStr string) uint { 168 sizeStr = strings.TrimSpace(sizeStr) 169 lastChar := len(sizeStr) - 1 170 multiplier := uint(1) 171 172 if lastChar > 0 { 173 if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' { 174 if lastChar > 1 { 175 switch unicode.ToLower(rune(sizeStr[lastChar-1])) { 176 case 'k': 177 multiplier = 1 << 10 178 sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) 179 case 'm': 180 multiplier = 1 << 20 181 sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) 182 case 'g': 183 multiplier = 1 << 30 184 sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) 185 default: 186 multiplier = 1 187 sizeStr = strings.TrimSpace(sizeStr[:lastChar]) 188 } 189 } 190 } 191 } 192 193 size := cast.ToInt(sizeStr) 194 if size < 0 { 195 size = 0 196 } 197 198 return safeMul(uint(size), multiplier) 199 } 200 201 // deepSearch scans deep maps, following the key indexes listed in the 202 // sequence "path". 203 // The last value is expected to be another map, and is returned. 204 // 205 // In case intermediate keys do not exist, or map to a non-map value, 206 // a new map is created and inserted, and the search continues from there: 207 // the initial map "m" may be modified! 208 func deepSearch(m map[string]interface{}, path []string) map[string]interface{} { 209 for _, k := range path { 210 m2, ok := m[k] 211 if !ok { 212 // intermediate key does not exist 213 // => create it and continue from there 214 m3 := make(map[string]interface{}) 215 m[k] = m3 216 m = m3 217 continue 218 } 219 m3, ok := m2.(map[string]interface{}) 220 if !ok { 221 // intermediate key is a value 222 // => replace with a new map 223 m3 = make(map[string]interface{}) 224 m[k] = m3 225 } 226 // continue search from here 227 m = m3 228 } 229 return m 230 }