github.com/andrewseidl/viper@v0.0.0-20191228040610-757ecc9b7b3e/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  	"bytes"
    15  	"encoding/json"
    16  	"fmt"
    17  	"io"
    18  	"os"
    19  	"path/filepath"
    20  	"runtime"
    21  	"strings"
    22  	"unicode"
    23  
    24  	"github.com/magiconair/properties"
    25  	toml "github.com/pelletier/go-toml"
    26  	"github.com/spf13/cast"
    27  	jww "github.com/spf13/jwalterweatherman"
    28  	"gopkg.in/yaml.v2"
    29  )
    30  
    31  // ConfigParseError denotes failing to parse configuration file.
    32  type ConfigParseError struct {
    33  	err error
    34  }
    35  
    36  // Error returns the formatted configuration error.
    37  func (pe ConfigParseError) Error() string {
    38  	return fmt.Sprintf("While parsing config: %s", pe.err.Error())
    39  }
    40  
    41  // toCaseInsensitiveValue checks if the value is a  map;
    42  // if so, create a copy and lower-case the keys recursively.
    43  func toCaseInsensitiveValue(value interface{}) interface{} {
    44  	switch v := value.(type) {
    45  	case map[interface{}]interface{}:
    46  		value = copyAndInsensitiviseMap(cast.ToStringMap(v))
    47  	case map[string]interface{}:
    48  		value = copyAndInsensitiviseMap(v)
    49  	}
    50  
    51  	return value
    52  }
    53  
    54  // copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
    55  // any map it makes case insensitive.
    56  func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
    57  	nm := make(map[string]interface{})
    58  
    59  	for key, val := range m {
    60  		lkey := strings.ToLower(key)
    61  		switch v := val.(type) {
    62  		case map[interface{}]interface{}:
    63  			nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
    64  		case map[string]interface{}:
    65  			nm[lkey] = copyAndInsensitiviseMap(v)
    66  		default:
    67  			nm[lkey] = v
    68  		}
    69  	}
    70  
    71  	return nm
    72  }
    73  
    74  func insensitiviseMap(m map[string]interface{}) {
    75  	for key, val := range m {
    76  		switch val.(type) {
    77  		case map[interface{}]interface{}:
    78  			// nested map: cast and recursively insensitivise
    79  			val = cast.ToStringMap(val)
    80  			insensitiviseMap(val.(map[string]interface{}))
    81  		case map[string]interface{}:
    82  			// nested map: recursively insensitivise
    83  			insensitiviseMap(val.(map[string]interface{}))
    84  		}
    85  
    86  		lower := strings.ToLower(key)
    87  		if key != lower {
    88  			// remove old key (not lower-cased)
    89  			delete(m, key)
    90  		}
    91  		// update map
    92  		m[lower] = val
    93  	}
    94  }
    95  
    96  func absPathify(inPath string) string {
    97  	jww.INFO.Println("Trying to resolve absolute path to", inPath)
    98  
    99  	if strings.HasPrefix(inPath, "$HOME") {
   100  		inPath = userHomeDir() + inPath[5:]
   101  	}
   102  
   103  	if strings.HasPrefix(inPath, "$") {
   104  		end := strings.Index(inPath, string(os.PathSeparator))
   105  		inPath = os.Getenv(inPath[1:end]) + inPath[end:]
   106  	}
   107  
   108  	if filepath.IsAbs(inPath) {
   109  		return filepath.Clean(inPath)
   110  	}
   111  
   112  	p, err := filepath.Abs(inPath)
   113  	if err == nil {
   114  		return filepath.Clean(p)
   115  	}
   116  
   117  	jww.ERROR.Println("Couldn't discover absolute path")
   118  	jww.ERROR.Println(err)
   119  	return ""
   120  }
   121  
   122  // Check if File / Directory Exists
   123  func exists(path string) (bool, error) {
   124  	_, err := v.fs.Stat(path)
   125  	if err == nil {
   126  		return true, nil
   127  	}
   128  	if os.IsNotExist(err) {
   129  		return false, nil
   130  	}
   131  	return false, err
   132  }
   133  
   134  func stringInSlice(a string, list []string) bool {
   135  	for _, b := range list {
   136  		if b == a {
   137  			return true
   138  		}
   139  	}
   140  	return false
   141  }
   142  
   143  func userHomeDir() string {
   144  	if runtime.GOOS == "windows" {
   145  		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
   146  		if home == "" {
   147  			home = os.Getenv("USERPROFILE")
   148  		}
   149  		return home
   150  	}
   151  	return os.Getenv("HOME")
   152  }
   153  
   154  func unmarshallConfigReader(in io.Reader, c map[string]interface{}, configType string) error {
   155  	buf := new(bytes.Buffer)
   156  	buf.ReadFrom(in)
   157  
   158  	switch strings.ToLower(configType) {
   159  	case "yaml", "yml":
   160  		if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil {
   161  			return ConfigParseError{err}
   162  		}
   163  
   164  	case "json":
   165  		if err := json.Unmarshal(buf.Bytes(), &c); err != nil {
   166  			return ConfigParseError{err}
   167  		}
   168  
   169  	case "toml":
   170  		tree, err := toml.LoadReader(buf)
   171  		if err != nil {
   172  			return ConfigParseError{err}
   173  		}
   174  		tmap := tree.ToMap()
   175  		for k, v := range tmap {
   176  			c[k] = v
   177  		}
   178  
   179  	case "properties", "props", "prop":
   180  		var p *properties.Properties
   181  		var err error
   182  		if p, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
   183  			return ConfigParseError{err}
   184  		}
   185  		for _, key := range p.Keys() {
   186  			value, _ := p.Get(key)
   187  			// recursively build nested maps
   188  			path := strings.Split(key, ".")
   189  			lastKey := strings.ToLower(path[len(path)-1])
   190  			deepestMap := deepSearch(c, path[0:len(path)-1])
   191  			// set innermost value
   192  			deepestMap[lastKey] = value
   193  		}
   194  	}
   195  
   196  	insensitiviseMap(c)
   197  	return nil
   198  }
   199  
   200  func safeMul(a, b uint) uint {
   201  	c := a * b
   202  	if a > 1 && b > 1 && c/b != a {
   203  		return 0
   204  	}
   205  	return c
   206  }
   207  
   208  // parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
   209  func parseSizeInBytes(sizeStr string) uint {
   210  	sizeStr = strings.TrimSpace(sizeStr)
   211  	lastChar := len(sizeStr) - 1
   212  	multiplier := uint(1)
   213  
   214  	if lastChar > 0 {
   215  		if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
   216  			if lastChar > 1 {
   217  				switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
   218  				case 'k':
   219  					multiplier = 1 << 10
   220  					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
   221  				case 'm':
   222  					multiplier = 1 << 20
   223  					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
   224  				case 'g':
   225  					multiplier = 1 << 30
   226  					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
   227  				default:
   228  					multiplier = 1
   229  					sizeStr = strings.TrimSpace(sizeStr[:lastChar])
   230  				}
   231  			}
   232  		}
   233  	}
   234  
   235  	size := cast.ToInt(sizeStr)
   236  	if size < 0 {
   237  		size = 0
   238  	}
   239  
   240  	return safeMul(uint(size), multiplier)
   241  }
   242  
   243  // deepSearch scans deep maps, following the key indexes listed in the
   244  // sequence "path".
   245  // The last value is expected to be another map, and is returned.
   246  //
   247  // In case intermediate keys do not exist, or map to a non-map value,
   248  // a new map is created and inserted, and the search continues from there:
   249  // the initial map "m" may be modified!
   250  func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
   251  	for _, k := range path {
   252  		m2, ok := m[k]
   253  		if !ok {
   254  			// intermediate key does not exist
   255  			// => create it and continue from there
   256  			m3 := make(map[string]interface{})
   257  			m[k] = m3
   258  			m = m3
   259  			continue
   260  		}
   261  		m3, ok := m2.(map[string]interface{})
   262  		if !ok {
   263  			// intermediate key is a value
   264  			// => replace with a new map
   265  			m3 = make(map[string]interface{})
   266  			m[k] = m3
   267  		}
   268  		// continue search from here
   269  		m = m3
   270  	}
   271  	return m
   272  }