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