OSDN Git Service

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