aba09bb853
Rounding is done using the suffix boundary (i.e. floating point value with `gb` suffix will return size which is an integer number of megabytes). Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
225 lines
5.3 KiB
Go
225 lines
5.3 KiB
Go
package config
|
|
|
|
import (
|
|
"math/bits"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
|
|
"github.com/spf13/cast"
|
|
)
|
|
|
|
func panicOnErr(err error) {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// StringSlice reads configuration value
|
|
// from c by name and casts it to []string.
|
|
//
|
|
// Panics if value can not be casted.
|
|
func StringSlice(c *Config, name string) []string {
|
|
x, err := cast.ToStringSliceE(c.Value(name))
|
|
panicOnErr(err)
|
|
|
|
return x
|
|
}
|
|
|
|
// StringSliceSafe reads configuration value
|
|
// from c by name and casts it to []string.
|
|
//
|
|
// Returns nil if value can not be casted.
|
|
func StringSliceSafe(c *Config, name string) []string {
|
|
return cast.ToStringSlice(c.Value(name))
|
|
}
|
|
|
|
// String reads configuration value
|
|
// from c by name and casts it to string.
|
|
//
|
|
// Panics if value can not be casted.
|
|
func String(c *Config, name string) string {
|
|
x, err := cast.ToStringE(c.Value(name))
|
|
panicOnErr(err)
|
|
|
|
return x
|
|
}
|
|
|
|
// StringSafe reads configuration value
|
|
// from c by name and casts it to string.
|
|
//
|
|
// Returns "" if value can not be casted.
|
|
func StringSafe(c *Config, name string) string {
|
|
return cast.ToString(c.Value(name))
|
|
}
|
|
|
|
// Duration reads configuration value
|
|
// from c by name and casts it to time.Duration.
|
|
//
|
|
// Panics if value can not be casted.
|
|
func Duration(c *Config, name string) time.Duration {
|
|
x, err := cast.ToDurationE(c.Value(name))
|
|
panicOnErr(err)
|
|
|
|
return x
|
|
}
|
|
|
|
// DurationSafe reads configuration value
|
|
// from c by name and casts it to time.Duration.
|
|
//
|
|
// Returns 0 if value can not be casted.
|
|
func DurationSafe(c *Config, name string) time.Duration {
|
|
return cast.ToDuration(c.Value(name))
|
|
}
|
|
|
|
// Bool reads configuration value
|
|
// from c by name and casts it to bool.
|
|
//
|
|
// Panics if value can not be casted.
|
|
func Bool(c *Config, name string) bool {
|
|
x, err := cast.ToBoolE(c.Value(name))
|
|
panicOnErr(err)
|
|
|
|
return x
|
|
}
|
|
|
|
// BoolSafe reads configuration value
|
|
// from c by name and casts it to bool.
|
|
//
|
|
// Returns false if value can not be casted.
|
|
func BoolSafe(c *Config, name string) bool {
|
|
return cast.ToBool(c.Value(name))
|
|
}
|
|
|
|
// Uint32 reads configuration value
|
|
// from c by name and casts it to uint32.
|
|
//
|
|
// Panics if value can not be casted.
|
|
func Uint32(c *Config, name string) uint32 {
|
|
x, err := cast.ToUint32E(c.Value(name))
|
|
panicOnErr(err)
|
|
|
|
return x
|
|
}
|
|
|
|
// Uint32Safe reads configuration value
|
|
// from c by name and casts it to uint32.
|
|
//
|
|
// Returns 0 if value can not be casted.
|
|
func Uint32Safe(c *Config, name string) uint32 {
|
|
return cast.ToUint32(c.Value(name))
|
|
}
|
|
|
|
// Uint reads configuration value
|
|
// from c by name and casts it to uint64.
|
|
//
|
|
// Panics if value can not be casted.
|
|
func Uint(c *Config, name string) uint64 {
|
|
x, err := cast.ToUint64E(c.Value(name))
|
|
panicOnErr(err)
|
|
|
|
return x
|
|
}
|
|
|
|
// UintSafe reads configuration value
|
|
// from c by name and casts it to uint64.
|
|
//
|
|
// Returns 0 if value can not be casted.
|
|
func UintSafe(c *Config, name string) uint64 {
|
|
return cast.ToUint64(c.Value(name))
|
|
}
|
|
|
|
// Int reads configuration value
|
|
// from c by name and casts it to int64.
|
|
//
|
|
// Panics if value can not be casted.
|
|
func Int(c *Config, name string) int64 {
|
|
x, err := cast.ToInt64E(c.Value(name))
|
|
panicOnErr(err)
|
|
|
|
return x
|
|
}
|
|
|
|
// IntSafe reads configuration value
|
|
// from c by name and casts it to int64.
|
|
//
|
|
// Returns 0 if value can not be casted.
|
|
func IntSafe(c *Config, name string) int64 {
|
|
return cast.ToInt64(c.Value(name))
|
|
}
|
|
|
|
// SizeInBytesSafe reads configuration value
|
|
// from c by name and casts it to size in bytes (uint64).
|
|
//
|
|
// The suffix can be single-letter (b, k, m, g, t) or with
|
|
// an additional b at the end. Spaces between the number and the suffix
|
|
// are allowed. All multipliers are power of 2 (i.e. k is for kibi-byte).
|
|
//
|
|
// Returns 0 if a value can't be casted.
|
|
func SizeInBytesSafe(c *Config, name string) uint64 {
|
|
s := StringSafe(c, name)
|
|
return parseSizeInBytes(s)
|
|
}
|
|
|
|
// The following code is taken from https://github.com/spf13/viper/blob/master/util.go
|
|
// with minor corrections (allow to use both `k` and `kb` forms.
|
|
// Seems like viper allows to convert sizes but corresponding parser in `cast` package
|
|
// is missing.
|
|
|
|
// safeMul returns size*multiplier, rounding down to the
|
|
// multiplier/1024 number of bytes.
|
|
// Returns 0 if overflow is detected.
|
|
func safeMul(size float64, multiplier uint64) uint64 {
|
|
n := uint64(size)
|
|
f := uint64((size - float64(n)) * 1024)
|
|
if f != 0 && multiplier != 1 {
|
|
s := n<<10 + f
|
|
if s < n {
|
|
return 0
|
|
}
|
|
|
|
n = s
|
|
multiplier >>= 10
|
|
}
|
|
|
|
hi, lo := bits.Mul64(n, multiplier)
|
|
if hi != 0 {
|
|
return 0
|
|
}
|
|
return lo
|
|
}
|
|
|
|
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
|
|
func parseSizeInBytes(sizeStr string) uint64 {
|
|
sizeStr = strings.TrimSpace(sizeStr)
|
|
lastChar := len(sizeStr) - 1
|
|
multiplier := uint64(1)
|
|
|
|
if lastChar > 0 {
|
|
if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
|
|
lastChar--
|
|
}
|
|
if lastChar > 0 {
|
|
switch unicode.ToLower(rune(sizeStr[lastChar])) {
|
|
case 'k':
|
|
multiplier = 1 << 10
|
|
sizeStr = strings.TrimSpace(sizeStr[:lastChar])
|
|
case 'm':
|
|
multiplier = 1 << 20
|
|
sizeStr = strings.TrimSpace(sizeStr[:lastChar])
|
|
case 'g':
|
|
multiplier = 1 << 30
|
|
sizeStr = strings.TrimSpace(sizeStr[:lastChar])
|
|
case 't':
|
|
multiplier = 1 << 40
|
|
sizeStr = strings.TrimSpace(sizeStr[:lastChar])
|
|
default:
|
|
multiplier = 1
|
|
sizeStr = strings.TrimSpace(sizeStr[:lastChar+1])
|
|
}
|
|
}
|
|
}
|
|
|
|
size := cast.ToFloat64(sizeStr)
|
|
return safeMul(size, multiplier)
|
|
}
|