github.com/endophage/viper@v0.0.0-20160111150723-a212099cbe6f/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/BurntSushi/toml"
    25  	"github.com/hashicorp/hcl"
    26  	"github.com/magiconair/properties"
    27  	"github.com/spf13/cast"
    28  	jww "github.com/spf13/jwalterweatherman"
    29  	"gopkg.in/yaml.v2"
    30  )
    31  
    32  // Denotes failing to parse configuration file.
    33  type ConfigParseError struct {
    34  	err error
    35  }
    36  
    37  // Returns the formatted configuration error.
    38  func (pe ConfigParseError) Error() string {
    39  	return fmt.Sprintf("While parsing config: %s", pe.err.Error())
    40  }
    41  
    42  func insensitiviseMap(m map[string]interface{}) {
    43  	for key, val := range m {
    44  		lower := strings.ToLower(key)
    45  		if key != lower {
    46  			delete(m, key)
    47  			m[lower] = val
    48  		}
    49  	}
    50  }
    51  
    52  func absPathify(inPath string) string {
    53  	jww.INFO.Println("Trying to resolve absolute path to", inPath)
    54  
    55  	if strings.HasPrefix(inPath, "$HOME") {
    56  		inPath = userHomeDir() + inPath[5:]
    57  	}
    58  
    59  	if strings.HasPrefix(inPath, "$") {
    60  		end := strings.Index(inPath, string(os.PathSeparator))
    61  		inPath = os.Getenv(inPath[1:end]) + inPath[end:]
    62  	}
    63  
    64  	if filepath.IsAbs(inPath) {
    65  		return filepath.Clean(inPath)
    66  	}
    67  
    68  	p, err := filepath.Abs(inPath)
    69  	if err == nil {
    70  		return filepath.Clean(p)
    71  	} else {
    72  		jww.ERROR.Println("Couldn't discover absolute path")
    73  		jww.ERROR.Println(err)
    74  	}
    75  	return ""
    76  }
    77  
    78  // Check if File / Directory Exists
    79  func exists(path string) (bool, error) {
    80  	_, err := os.Stat(path)
    81  	if err == nil {
    82  		return true, nil
    83  	}
    84  	if os.IsNotExist(err) {
    85  		return false, nil
    86  	}
    87  	return false, err
    88  }
    89  
    90  func stringInSlice(a string, list []string) bool {
    91  	for _, b := range list {
    92  		if b == a {
    93  			return true
    94  		}
    95  	}
    96  	return false
    97  }
    98  
    99  func userHomeDir() string {
   100  	if runtime.GOOS == "windows" {
   101  		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
   102  		if home == "" {
   103  			home = os.Getenv("USERPROFILE")
   104  		}
   105  		return home
   106  	}
   107  	return os.Getenv("HOME")
   108  }
   109  
   110  func findCWD() (string, error) {
   111  	serverFile, err := filepath.Abs(os.Args[0])
   112  
   113  	if err != nil {
   114  		return "", fmt.Errorf("Can't get absolute path for executable: %v", err)
   115  	}
   116  
   117  	path := filepath.Dir(serverFile)
   118  	realFile, err := filepath.EvalSymlinks(serverFile)
   119  
   120  	if err != nil {
   121  		if _, err = os.Stat(serverFile + ".exe"); err == nil {
   122  			realFile = filepath.Clean(serverFile + ".exe")
   123  		}
   124  	}
   125  
   126  	if err == nil && realFile != serverFile {
   127  		path = filepath.Dir(realFile)
   128  	}
   129  
   130  	return path, nil
   131  }
   132  
   133  func unmarshallConfigReader(in io.Reader, c map[string]interface{}, configType string) error {
   134  	buf := new(bytes.Buffer)
   135  	buf.ReadFrom(in)
   136  
   137  	switch strings.ToLower(configType) {
   138  	case "yaml", "yml":
   139  		if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil {
   140  			return ConfigParseError{err}
   141  		}
   142  
   143  	case "json":
   144  		if err := json.Unmarshal(buf.Bytes(), &c); err != nil {
   145  			return ConfigParseError{err}
   146  		}
   147  
   148  	case "hcl":
   149  		obj, err := hcl.Parse(string(buf.Bytes()))
   150  		if err != nil {
   151  			return ConfigParseError{err}
   152  		}
   153  		if err = hcl.DecodeObject(&c, obj); err != nil {
   154  			return ConfigParseError{err}
   155  		}
   156  
   157  	case "toml":
   158  		if _, err := toml.Decode(buf.String(), &c); err != nil {
   159  			return ConfigParseError{err}
   160  		}
   161  
   162  	case "properties", "props", "prop":
   163  		var p *properties.Properties
   164  		var err error
   165  		if p, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
   166  			return ConfigParseError{err}
   167  		}
   168  		for _, key := range p.Keys() {
   169  			value, _ := p.Get(key)
   170  			c[key] = value
   171  		}
   172  	}
   173  
   174  	insensitiviseMap(c)
   175  	return nil
   176  }
   177  
   178  func safeMul(a, b uint) uint {
   179  	c := a * b
   180  	if a > 1 && b > 1 && c/b != a {
   181  		return 0
   182  	}
   183  	return c
   184  }
   185  
   186  // parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
   187  func parseSizeInBytes(sizeStr string) uint {
   188  	sizeStr = strings.TrimSpace(sizeStr)
   189  	lastChar := len(sizeStr) - 1
   190  	multiplier := uint(1)
   191  
   192  	if lastChar > 0 {
   193  		if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
   194  			if lastChar > 1 {
   195  				switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
   196  				case 'k':
   197  					multiplier = 1 << 10
   198  					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
   199  				case 'm':
   200  					multiplier = 1 << 20
   201  					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
   202  				case 'g':
   203  					multiplier = 1 << 30
   204  					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
   205  				default:
   206  					multiplier = 1
   207  					sizeStr = strings.TrimSpace(sizeStr[:lastChar])
   208  				}
   209  			}
   210  		}
   211  	}
   212  
   213  	size := cast.ToInt(sizeStr)
   214  	if size < 0 {
   215  		size = 0
   216  	}
   217  
   218  	return safeMul(uint(size), multiplier)
   219  }