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