Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/spf13/viper/util.go
2875 views
1
// Copyright © 2014 Steve Francia <[email protected]>.
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
"log/slog"
16
"os"
17
"path/filepath"
18
"runtime"
19
"strings"
20
"unicode"
21
22
"github.com/spf13/cast"
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
// Unwrap returns the wrapped error.
36
func (pe ConfigParseError) Unwrap() error {
37
return pe.err
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 any) any {
43
switch v := value.(type) {
44
case map[any]any:
45
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
46
case map[string]any:
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]any) map[string]any {
56
nm := make(map[string]any)
57
58
for key, val := range m {
59
lkey := strings.ToLower(key)
60
switch v := val.(type) {
61
case map[any]any:
62
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
63
case map[string]any:
64
nm[lkey] = copyAndInsensitiviseMap(v)
65
default:
66
nm[lkey] = v
67
}
68
}
69
70
return nm
71
}
72
73
func insensitiviseVal(val any) any {
74
switch v := val.(type) {
75
case map[any]any:
76
// nested map: cast and recursively insensitivise
77
val = cast.ToStringMap(val)
78
insensitiviseMap(val.(map[string]any))
79
case map[string]any:
80
// nested map: recursively insensitivise
81
insensitiviseMap(v)
82
case []any:
83
// nested array: recursively insensitivise
84
insensitiveArray(v)
85
}
86
return val
87
}
88
89
func insensitiviseMap(m map[string]any) {
90
for key, val := range m {
91
val = insensitiviseVal(val)
92
lower := strings.ToLower(key)
93
if key != lower {
94
// remove old key (not lower-cased)
95
delete(m, key)
96
}
97
// update map
98
m[lower] = val
99
}
100
}
101
102
func insensitiveArray(a []any) {
103
for i, val := range a {
104
a[i] = insensitiviseVal(val)
105
}
106
}
107
108
func absPathify(logger *slog.Logger, inPath string) string {
109
logger.Info("trying to resolve absolute path", "path", inPath)
110
111
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
112
inPath = userHomeDir() + inPath[5:]
113
}
114
115
inPath = os.ExpandEnv(inPath)
116
117
if filepath.IsAbs(inPath) {
118
return filepath.Clean(inPath)
119
}
120
121
p, err := filepath.Abs(inPath)
122
if err == nil {
123
return filepath.Clean(p)
124
}
125
126
logger.Error(fmt.Errorf("could not discover absolute path: %w", err).Error())
127
128
return ""
129
}
130
131
func userHomeDir() string {
132
if runtime.GOOS == "windows" {
133
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
134
if home == "" {
135
home = os.Getenv("USERPROFILE")
136
}
137
return home
138
}
139
return os.Getenv("HOME")
140
}
141
142
func safeMul(a, b uint) uint {
143
c := a * b
144
if a > 1 && b > 1 && c/b != a {
145
return 0
146
}
147
return c
148
}
149
150
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes.
151
func parseSizeInBytes(sizeStr string) uint {
152
sizeStr = strings.TrimSpace(sizeStr)
153
lastChar := len(sizeStr) - 1
154
multiplier := uint(1)
155
156
if lastChar > 0 {
157
if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
158
if lastChar > 1 {
159
switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
160
case 'k':
161
multiplier = 1 << 10
162
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
163
case 'm':
164
multiplier = 1 << 20
165
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
166
case 'g':
167
multiplier = 1 << 30
168
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
169
default:
170
multiplier = 1
171
sizeStr = strings.TrimSpace(sizeStr[:lastChar])
172
}
173
}
174
}
175
}
176
177
size := max(cast.ToInt(sizeStr), 0)
178
179
return safeMul(uint(size), multiplier)
180
}
181
182
// deepSearch scans deep maps, following the key indexes listed in the
183
// sequence "path".
184
// The last value is expected to be another map, and is returned.
185
//
186
// In case intermediate keys do not exist, or map to a non-map value,
187
// a new map is created and inserted, and the search continues from there:
188
// the initial map "m" may be modified!
189
func deepSearch(m map[string]any, path []string) map[string]any {
190
for _, k := range path {
191
m2, ok := m[k]
192
if !ok {
193
// intermediate key does not exist
194
// => create it and continue from there
195
m3 := make(map[string]any)
196
m[k] = m3
197
m = m3
198
continue
199
}
200
m3, ok := m2.(map[string]any)
201
if !ok {
202
// intermediate key is a value
203
// => replace with a new map
204
m3 = make(map[string]any)
205
m[k] = m3
206
}
207
// continue search from here
208
m = m3
209
}
210
return m
211
}
212
213