gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

util.go (5376B)


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