github.com/DataDog/viper@v1.13.3/util.go (about)

     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 strings.HasPrefix(inPath, "$HOME") {
    95  		inPath = userHomeDir() + inPath[5:]
    96  	}
    97  
    98  	if strings.HasPrefix(inPath, "$") {
    99  		end := strings.Index(inPath, string(os.PathSeparator))
   100  		inPath = os.Getenv(inPath[1:end]) + inPath[end:]
   101  	}
   102  
   103  	if filepath.IsAbs(inPath) {
   104  		return filepath.Clean(inPath)
   105  	}
   106  
   107  	p, err := filepath.Abs(inPath)
   108  	if err == nil {
   109  		return filepath.Clean(p)
   110  	}
   111  
   112  	jww.ERROR.Println("Couldn't discover absolute path")
   113  	jww.ERROR.Println(err)
   114  	return ""
   115  }
   116  
   117  // Check if File / Directory Exists
   118  func exists(fs afero.Fs, path string) (bool, error) {
   119  	_, err := fs.Stat(path)
   120  	if err == nil {
   121  		return true, nil
   122  	}
   123  	if os.IsNotExist(err) {
   124  		return false, nil
   125  	}
   126  	return false, err
   127  }
   128  
   129  func stringInSlice(a string, list []string) bool {
   130  	for _, b := range list {
   131  		if b == a {
   132  			return true
   133  		}
   134  	}
   135  	return false
   136  }
   137  
   138  func userHomeDir() string {
   139  	if runtime.GOOS == "windows" {
   140  		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
   141  		if home == "" {
   142  			home = os.Getenv("USERPROFILE")
   143  		}
   144  		return home
   145  	}
   146  	return os.Getenv("HOME")
   147  }
   148  
   149  func safeMul(a, b uint) (uint, error) {
   150  	c := a * b
   151  	if a > 1 && b > 1 && c/b != a {
   152  		return 0, fmt.Errorf("multiplication overflows uint: %d*%d", a, b)
   153  	}
   154  	return c, nil
   155  }
   156  
   157  // parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
   158  func parseSizeInBytes(sizeStr string) (uint, error) {
   159  	rawStr := sizeStr
   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  	num, err := cast.ToUintE(sizeStr)
   186  	if err != nil {
   187  		return 0, err
   188  	}
   189  
   190  	size, err := safeMul(num, multiplier)
   191  	if err != nil {
   192  		return 0, fmt.Errorf("unable to cast %q to uint: %s", rawStr, err)
   193  	}
   194  
   195  	return size, nil
   196  }
   197  
   198  // deepSearch scans deep maps, following the key indexes listed in the
   199  // sequence "path".
   200  // The last value is expected to be another map, and is returned.
   201  //
   202  // In case intermediate keys do not exist, or map to a non-map value,
   203  // a new map is created and inserted, and the search continues from there:
   204  // the initial map "m" may be modified!
   205  func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
   206  	for _, k := range path {
   207  		m2, ok := m[k]
   208  		if !ok {
   209  			// intermediate key does not exist
   210  			// => create it and continue from there
   211  			m3 := make(map[string]interface{})
   212  			m[k] = m3
   213  			m = m3
   214  			continue
   215  		}
   216  		m3, ok := m2.(map[string]interface{})
   217  		if !ok {
   218  			// intermediate key is a value
   219  			// => replace with a new map
   220  			m3 = make(map[string]interface{})
   221  			m[k] = m3
   222  		}
   223  		// continue search from here
   224  		m = m3
   225  	}
   226  	return m
   227  }