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