util.go - viper - [fork] go viper port for 9front
(HTM) git clone https://git.drkhsh.at/viper.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
util.go (5502B)
---
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 }