Path: blob/main/vendor/github.com/spf13/viper/util.go
2875 views
// Copyright © 2014 Steve Francia <[email protected]>.1//2// Use of this source code is governed by an MIT-style3// license that can be found in the LICENSE file.45// Viper is a application configuration system.6// It believes that applications can be configured a variety of ways7// via flags, ENVIRONMENT variables, configuration files retrieved8// from the file system, or a remote key/value store.910package viper1112import (13"fmt"14"log/slog"15"os"16"path/filepath"17"runtime"18"strings"19"unicode"2021"github.com/spf13/cast"22)2324// ConfigParseError denotes failing to parse configuration file.25type ConfigParseError struct {26err error27}2829// Error returns the formatted configuration error.30func (pe ConfigParseError) Error() string {31return fmt.Sprintf("While parsing config: %s", pe.err.Error())32}3334// Unwrap returns the wrapped error.35func (pe ConfigParseError) Unwrap() error {36return pe.err37}3839// toCaseInsensitiveValue checks if the value is a map;40// if so, create a copy and lower-case the keys recursively.41func toCaseInsensitiveValue(value any) any {42switch v := value.(type) {43case map[any]any:44value = copyAndInsensitiviseMap(cast.ToStringMap(v))45case map[string]any:46value = copyAndInsensitiviseMap(v)47}4849return value50}5152// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of53// any map it makes case insensitive.54func copyAndInsensitiviseMap(m map[string]any) map[string]any {55nm := make(map[string]any)5657for key, val := range m {58lkey := strings.ToLower(key)59switch v := val.(type) {60case map[any]any:61nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))62case map[string]any:63nm[lkey] = copyAndInsensitiviseMap(v)64default:65nm[lkey] = v66}67}6869return nm70}7172func insensitiviseVal(val any) any {73switch v := val.(type) {74case map[any]any:75// nested map: cast and recursively insensitivise76val = cast.ToStringMap(val)77insensitiviseMap(val.(map[string]any))78case map[string]any:79// nested map: recursively insensitivise80insensitiviseMap(v)81case []any:82// nested array: recursively insensitivise83insensitiveArray(v)84}85return val86}8788func insensitiviseMap(m map[string]any) {89for key, val := range m {90val = insensitiviseVal(val)91lower := strings.ToLower(key)92if key != lower {93// remove old key (not lower-cased)94delete(m, key)95}96// update map97m[lower] = val98}99}100101func insensitiveArray(a []any) {102for i, val := range a {103a[i] = insensitiviseVal(val)104}105}106107func absPathify(logger *slog.Logger, inPath string) string {108logger.Info("trying to resolve absolute path", "path", inPath)109110if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {111inPath = userHomeDir() + inPath[5:]112}113114inPath = os.ExpandEnv(inPath)115116if filepath.IsAbs(inPath) {117return filepath.Clean(inPath)118}119120p, err := filepath.Abs(inPath)121if err == nil {122return filepath.Clean(p)123}124125logger.Error(fmt.Errorf("could not discover absolute path: %w", err).Error())126127return ""128}129130func userHomeDir() string {131if runtime.GOOS == "windows" {132home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")133if home == "" {134home = os.Getenv("USERPROFILE")135}136return home137}138return os.Getenv("HOME")139}140141func safeMul(a, b uint) uint {142c := a * b143if a > 1 && b > 1 && c/b != a {144return 0145}146return c147}148149// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes.150func parseSizeInBytes(sizeStr string) uint {151sizeStr = strings.TrimSpace(sizeStr)152lastChar := len(sizeStr) - 1153multiplier := uint(1)154155if lastChar > 0 {156if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {157if lastChar > 1 {158switch unicode.ToLower(rune(sizeStr[lastChar-1])) {159case 'k':160multiplier = 1 << 10161sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])162case 'm':163multiplier = 1 << 20164sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])165case 'g':166multiplier = 1 << 30167sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])168default:169multiplier = 1170sizeStr = strings.TrimSpace(sizeStr[:lastChar])171}172}173}174}175176size := max(cast.ToInt(sizeStr), 0)177178return safeMul(uint(size), multiplier)179}180181// deepSearch scans deep maps, following the key indexes listed in the182// sequence "path".183// The last value is expected to be another map, and is returned.184//185// In case intermediate keys do not exist, or map to a non-map value,186// a new map is created and inserted, and the search continues from there:187// the initial map "m" may be modified!188func deepSearch(m map[string]any, path []string) map[string]any {189for _, k := range path {190m2, ok := m[k]191if !ok {192// intermediate key does not exist193// => create it and continue from there194m3 := make(map[string]any)195m[k] = m3196m = m3197continue198}199m3, ok := m2.(map[string]any)200if !ok {201// intermediate key is a value202// => replace with a new map203m3 = make(map[string]any)204m[k] = m3205}206// continue search from here207m = m3208}209return m210}211212213