util.go 4.7 KB

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