Upstream gookit/config state of the touched files before 8a81b3bce7
This commit is contained in:
commit
d54d79012b
3 files changed
+1201
No files matched your search
@@ -0,0 +1,684 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gookit/goutil/envutil"
|
||||||
|
"github.com/gookit/goutil/maputil"
|
||||||
|
"github.com/gookit/goutil/mathutil"
|
||||||
|
"github.com/gookit/goutil/strutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exists key exists check
|
||||||
|
func Exists(key string, findByPath ...bool) bool { return dc.Exists(key, findByPath...) }
|
||||||
|
|
||||||
|
// Exists key exists check
|
||||||
|
func (c *Config) Exists(key string, findByPath ...bool) (ok bool) {
|
||||||
|
sep := c.opts.Delimiter
|
||||||
|
if key = formatKey(key, string(sep)); key == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok = c.data[key]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable find by path.
|
||||||
|
if len(findByPath) > 0 && !findByPath[0] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// has sub key? eg. "lang.dir"
|
||||||
|
if strings.IndexByte(key, sep) == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := strings.Split(key, string(sep))
|
||||||
|
topK := keys[0]
|
||||||
|
|
||||||
|
// find top item data based on top key
|
||||||
|
var item any
|
||||||
|
if item, ok = c.data[topK]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, k := range keys[1:] {
|
||||||
|
switch typeData := item.(type) {
|
||||||
|
case map[string]int: // is map(from Set)
|
||||||
|
if item, ok = typeData[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case map[string]string: // is map(from Set)
|
||||||
|
if item, ok = typeData[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case map[string]any: // is map(decode from toml/json/yaml.v3)
|
||||||
|
if item, ok = typeData[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case map[any]any: // is map(decode from yaml.v2)
|
||||||
|
if item, ok = typeData[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case []int: // is array(is from Set)
|
||||||
|
i, err := strconv.Atoi(k)
|
||||||
|
if err != nil || i >= len(typeData) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
item = typeData[i]
|
||||||
|
case []string: // is array(is from Set)
|
||||||
|
i, err := strconv.Atoi(k)
|
||||||
|
if err != nil || i >= len(typeData) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
item = typeData[i]
|
||||||
|
case []any: // is array(load from file)
|
||||||
|
i, err := strconv.Atoi(k)
|
||||||
|
if err != nil || i >= len(typeData) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
item = typeData[i]
|
||||||
|
default: // error
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
* read config data
|
||||||
|
*************************************************************/
|
||||||
|
|
||||||
|
// Data return all config data
|
||||||
|
func Data() map[string]any { return dc.Data() }
|
||||||
|
|
||||||
|
// Data get all config data.
|
||||||
|
//
|
||||||
|
// Note: will don't apply any options, like ParseEnv
|
||||||
|
func (c *Config) Data() map[string]any {
|
||||||
|
return c.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub return a map config data by key
|
||||||
|
func Sub(key string) map[string]any { return dc.Sub(key) }
|
||||||
|
|
||||||
|
// Sub get a map config data by key
|
||||||
|
//
|
||||||
|
// Note: will don't apply any options, like ParseEnv
|
||||||
|
func (c *Config) Sub(key string) map[string]any {
|
||||||
|
if mp, ok := c.GetValue(key); ok {
|
||||||
|
if mmp, ok := mp.(map[string]any); ok {
|
||||||
|
return mmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys return all config data
|
||||||
|
func Keys() []string { return dc.Keys() }
|
||||||
|
|
||||||
|
// Keys get all config data
|
||||||
|
func (c *Config) Keys() []string {
|
||||||
|
keys := make([]string, 0, len(c.data))
|
||||||
|
for key := range c.data {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get config value by key string, support get sub-value by key path(eg. 'map.key'),
|
||||||
|
func Get(key string, findByPath ...bool) any { return dc.Get(key, findByPath...) }
|
||||||
|
|
||||||
|
// Get config value by key, findByPath default is true.
|
||||||
|
func (c *Config) Get(key string, findByPath ...bool) any {
|
||||||
|
val, _ := c.GetValue(key, findByPath...)
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValue get value by given key string. findByPath default is true.
|
||||||
|
func GetValue(key string, findByPath ...bool) (any, bool) {
|
||||||
|
return dc.GetValue(key, findByPath...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValue get value by given key string. findByPath default is true.
|
||||||
|
//
|
||||||
|
// Return:
|
||||||
|
// - ok is true, find value from config
|
||||||
|
// - ok is false, not found or error
|
||||||
|
func (c *Config) GetValue(key string, findByPath ...bool) (value any, ok bool) {
|
||||||
|
sep := c.opts.Delimiter
|
||||||
|
if key = formatKey(key, string(sep)); key == "" {
|
||||||
|
c.addError(ErrKeyIsEmpty)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not is readonly
|
||||||
|
if !c.opts.Readonly {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// is top key
|
||||||
|
if value, ok = c.data[key]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable find by path.
|
||||||
|
if len(findByPath) > 0 && !findByPath[0] {
|
||||||
|
// c.addError(ErrNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// has sub key? eg. "lang.dir"
|
||||||
|
if strings.IndexByte(key, sep) == -1 {
|
||||||
|
// c.addError(ErrNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := strings.Split(key, string(sep))
|
||||||
|
topK := keys[0]
|
||||||
|
|
||||||
|
// find top item data based on top key
|
||||||
|
var item any
|
||||||
|
if item, ok = c.data[topK]; !ok {
|
||||||
|
// c.addError(ErrNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// find child
|
||||||
|
// NOTICE: don't merge case, will result in an error.
|
||||||
|
// e.g. case []int, []string
|
||||||
|
// OR
|
||||||
|
// case []int:
|
||||||
|
// case []string:
|
||||||
|
for _, k := range keys[1:] {
|
||||||
|
switch typeData := item.(type) {
|
||||||
|
case map[string]int: // is map(from Set)
|
||||||
|
if item, ok = typeData[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case map[string]string: // is map(from Set)
|
||||||
|
if item, ok = typeData[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case map[string]any: // is map(decode from toml/json)
|
||||||
|
if item, ok = typeData[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case map[any]any: // is map(decode from yaml)
|
||||||
|
if item, ok = typeData[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case []int: // is array(is from Set)
|
||||||
|
i, err := strconv.Atoi(k)
|
||||||
|
|
||||||
|
// check slice index
|
||||||
|
if err != nil || len(typeData) <= i {
|
||||||
|
ok = false
|
||||||
|
c.addError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item = typeData[i]
|
||||||
|
case []string: // is array(is from Set)
|
||||||
|
i, err := strconv.Atoi(k)
|
||||||
|
if err != nil || len(typeData) <= i {
|
||||||
|
ok = false
|
||||||
|
c.addError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item = typeData[i]
|
||||||
|
case []any: // is array(load from file)
|
||||||
|
i, err := strconv.Atoi(k)
|
||||||
|
if err != nil || len(typeData) <= i {
|
||||||
|
ok = false
|
||||||
|
c.addError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item = typeData[i]
|
||||||
|
default: // error
|
||||||
|
ok = false
|
||||||
|
c.addErrorf("cannot get value of the key '%s'", key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
* read config (basic data type)
|
||||||
|
*************************************************************/
|
||||||
|
|
||||||
|
// String get a string by key
|
||||||
|
func String(key string, defVal ...string) string { return dc.String(key, defVal...) }
|
||||||
|
|
||||||
|
// String get a string by key, if not found return default value
|
||||||
|
func (c *Config) String(key string, defVal ...string) string {
|
||||||
|
value, ok := c.getString(key)
|
||||||
|
|
||||||
|
if !ok && len(defVal) > 0 { // give default value
|
||||||
|
value = defVal[0]
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustString get a string by key, will panic on empty or not exists
|
||||||
|
func MustString(key string) string { return dc.MustString(key) }
|
||||||
|
|
||||||
|
// MustString get a string by key, will panic on empty or not exists
|
||||||
|
func (c *Config) MustString(key string) string {
|
||||||
|
value, ok := c.getString(key)
|
||||||
|
if !ok {
|
||||||
|
panic("config: string value not found, key: " + key)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) getString(key string) (value string, ok bool) {
|
||||||
|
// find from cache
|
||||||
|
if c.opts.EnableCache && len(c.strCache) > 0 {
|
||||||
|
value, ok = c.strCache[key]
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val, ok := c.GetValue(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typVal := val.(type) {
|
||||||
|
// from json `int` always is float64
|
||||||
|
case string:
|
||||||
|
value = typVal
|
||||||
|
if c.opts.ParseEnv {
|
||||||
|
value = envutil.ParseEnvValue(value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
var err error
|
||||||
|
value, err = strutil.AnyToString(val, false)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add cache
|
||||||
|
if ok && c.opts.EnableCache {
|
||||||
|
if c.strCache == nil {
|
||||||
|
c.strCache = make(map[string]string)
|
||||||
|
}
|
||||||
|
c.strCache[key] = value
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int get an int by key
|
||||||
|
func Int(key string, defVal ...int) int { return dc.Int(key, defVal...) }
|
||||||
|
|
||||||
|
// Int get a int value, if not found return default value
|
||||||
|
func (c *Config) Int(key string, defVal ...int) (value int) {
|
||||||
|
i64, exist := c.tryInt64(key)
|
||||||
|
|
||||||
|
if exist {
|
||||||
|
value = int(i64)
|
||||||
|
} else if len(defVal) > 0 {
|
||||||
|
value = defVal[0]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint get a uint value, if not found return default value
|
||||||
|
func Uint(key string, defVal ...uint) uint { return dc.Uint(key, defVal...) }
|
||||||
|
|
||||||
|
// Uint get a int value, if not found return default value
|
||||||
|
func (c *Config) Uint(key string, defVal ...uint) (value uint) {
|
||||||
|
i64, exist := c.tryInt64(key)
|
||||||
|
|
||||||
|
if exist {
|
||||||
|
value = uint(i64)
|
||||||
|
} else if len(defVal) > 0 {
|
||||||
|
value = defVal[0]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 get a int value, if not found return default value
|
||||||
|
func Int64(key string, defVal ...int64) int64 { return dc.Int64(key, defVal...) }
|
||||||
|
|
||||||
|
// Int64 get a int value, if not found return default value
|
||||||
|
func (c *Config) Int64(key string, defVal ...int64) (value int64) {
|
||||||
|
value, exist := c.tryInt64(key)
|
||||||
|
|
||||||
|
if !exist && len(defVal) > 0 {
|
||||||
|
value = defVal[0]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to get an int64 value by given key
|
||||||
|
func (c *Config) tryInt64(key string) (value int64, ok bool) {
|
||||||
|
strVal, ok := c.getString(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := strconv.ParseInt(strVal, 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
c.addError(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration get a time.Duration type value. if not found return default value
|
||||||
|
func Duration(key string, defVal ...time.Duration) time.Duration { return dc.Duration(key, defVal...) }
|
||||||
|
|
||||||
|
// Duration get a time.Duration type value. if not found return default value.
|
||||||
|
//
|
||||||
|
// The stored value may be a Go duration string (e.g. "300s", "1h30m", "20m").
|
||||||
|
// A bare integer is treated as nanoseconds for backwards compatibility.
|
||||||
|
func (c *Config) Duration(key string, defVal ...time.Duration) time.Duration {
|
||||||
|
str, exist := c.getString(key)
|
||||||
|
if !exist {
|
||||||
|
if len(defVal) > 0 {
|
||||||
|
return defVal[0]
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer a Go duration string, e.g. "300s", "1h30m".
|
||||||
|
if dur, err := time.ParseDuration(str); err == nil {
|
||||||
|
return dur
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backwards compatible: a bare integer is nanoseconds.
|
||||||
|
if n, err := strconv.ParseInt(str, 10, 64); err == nil {
|
||||||
|
return time.Duration(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(defVal) > 0 {
|
||||||
|
return defVal[0]
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float get a float64 value, if not found return default value
|
||||||
|
func Float(key string, defVal ...float64) float64 { return dc.Float(key, defVal...) }
|
||||||
|
|
||||||
|
// Float get a float64 by key
|
||||||
|
func (c *Config) Float(key string, defVal ...float64) (value float64) {
|
||||||
|
str, ok := c.getString(key)
|
||||||
|
if !ok {
|
||||||
|
if len(defVal) > 0 {
|
||||||
|
value = defVal[0]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := strconv.ParseFloat(str, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.addError(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool get a bool value, if not found return default value
|
||||||
|
func Bool(key string, defVal ...bool) bool { return dc.Bool(key, defVal...) }
|
||||||
|
|
||||||
|
// Bool looks up a value for a key in this section and attempts to parse that value as a boolean,
|
||||||
|
// along with a boolean result similar to a map lookup.
|
||||||
|
//
|
||||||
|
// of following(case insensitive):
|
||||||
|
// - true
|
||||||
|
// - yes
|
||||||
|
// - false
|
||||||
|
// - no
|
||||||
|
// - 1
|
||||||
|
// - 0
|
||||||
|
//
|
||||||
|
// The `ok` boolean will be false in the event that the value could not be parsed as a bool
|
||||||
|
func (c *Config) Bool(key string, defVal ...bool) (value bool) {
|
||||||
|
rawVal, ok := c.getString(key)
|
||||||
|
if !ok {
|
||||||
|
if len(defVal) > 0 {
|
||||||
|
return defVal[0]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lowerCase := strings.ToLower(rawVal)
|
||||||
|
switch lowerCase {
|
||||||
|
case "", "0", "false", "no":
|
||||||
|
value = false
|
||||||
|
case "1", "true", "yes":
|
||||||
|
value = true
|
||||||
|
default:
|
||||||
|
c.addErrorf("the value '%s' cannot be convert to bool", lowerCase)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
* read config (complex data type)
|
||||||
|
*************************************************************/
|
||||||
|
|
||||||
|
// Ints get config data as an int slice/array
|
||||||
|
func Ints(key string) []int { return dc.Ints(key) }
|
||||||
|
|
||||||
|
// Ints get config data as an int slice/array
|
||||||
|
func (c *Config) Ints(key string) (arr []int) {
|
||||||
|
rawVal, ok := c.GetValue(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeData := rawVal.(type) {
|
||||||
|
case []int:
|
||||||
|
arr = typeData
|
||||||
|
case []any:
|
||||||
|
for _, v := range typeData {
|
||||||
|
iv, err := mathutil.ToInt(v)
|
||||||
|
// iv, err := strconv.Atoi(fmt.Sprintf("%v", v))
|
||||||
|
if err != nil {
|
||||||
|
c.addError(err)
|
||||||
|
arr = arr[0:0] // reset
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
arr = append(arr, iv)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
c.addErrorf("value cannot be convert to []int, key is '%s'", key)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntMap get config data as a map[string]int
|
||||||
|
func IntMap(key string) map[string]int { return dc.IntMap(key) }
|
||||||
|
|
||||||
|
// IntMap get config data as a map[string]int
|
||||||
|
func (c *Config) IntMap(key string) (mp map[string]int) {
|
||||||
|
rawVal, ok := c.GetValue(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeData := rawVal.(type) {
|
||||||
|
case map[string]int: // from Set
|
||||||
|
mp = typeData
|
||||||
|
case map[string]any: // decode from json,toml
|
||||||
|
mp = make(map[string]int)
|
||||||
|
for k, v := range typeData {
|
||||||
|
// iv, err := strconv.Atoi(fmt.Sprintf("%v", v))
|
||||||
|
iv, err := mathutil.ToInt(v)
|
||||||
|
if err != nil {
|
||||||
|
c.addError(err)
|
||||||
|
mp = map[string]int{} // reset
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mp[k] = iv
|
||||||
|
}
|
||||||
|
case map[any]any: // if decode from yaml
|
||||||
|
mp = make(map[string]int)
|
||||||
|
for k, v := range typeData {
|
||||||
|
// iv, err := strconv.Atoi(fmt.Sprintf( "%v", v))
|
||||||
|
iv, err := mathutil.ToInt(v)
|
||||||
|
if err != nil {
|
||||||
|
c.addError(err)
|
||||||
|
mp = map[string]int{} // reset
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// sk := fmt.Sprintf("%v", k)
|
||||||
|
sk, _ := strutil.AnyToString(k, false)
|
||||||
|
mp[sk] = iv
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
c.addErrorf("value cannot be convert to map[string]int, key is '%s'", key)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings get strings by key
|
||||||
|
func Strings(key string) []string { return dc.Strings(key) }
|
||||||
|
|
||||||
|
// Strings get config data as a string slice/array
|
||||||
|
func (c *Config) Strings(key string) (arr []string) {
|
||||||
|
var ok bool
|
||||||
|
// find from cache
|
||||||
|
if c.opts.EnableCache && len(c.sArrCache) > 0 {
|
||||||
|
arr, ok = c.sArrCache[key]
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rawVal, ok := c.GetValue(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeData := rawVal.(type) {
|
||||||
|
case []string:
|
||||||
|
arr = typeData
|
||||||
|
case []any:
|
||||||
|
for _, v := range typeData {
|
||||||
|
// arr = append(arr, fmt.Sprintf("%v", v))
|
||||||
|
arr = append(arr, strutil.MustString(v))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
c.addErrorf("value cannot be convert to []string, key is '%s'", key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// add cache
|
||||||
|
if c.opts.EnableCache {
|
||||||
|
if c.sArrCache == nil {
|
||||||
|
c.sArrCache = make(map[string]strArr)
|
||||||
|
}
|
||||||
|
c.sArrCache[key] = arr
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringsBySplit get []string by split a string value.
|
||||||
|
func StringsBySplit(key, sep string) []string { return dc.StringsBySplit(key, sep) }
|
||||||
|
|
||||||
|
// StringsBySplit get []string by split a string value.
|
||||||
|
func (c *Config) StringsBySplit(key, sep string) (ss []string) {
|
||||||
|
if str, ok := c.getString(key); ok {
|
||||||
|
ss = strutil.Split(str, sep)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringMap get config data as a map[string]string
|
||||||
|
func StringMap(key string) map[string]string { return dc.StringMap(key) }
|
||||||
|
|
||||||
|
// StringMap get config data as a map[string]string
|
||||||
|
func (c *Config) StringMap(key string) (mp map[string]string) {
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
// find from cache
|
||||||
|
if c.opts.EnableCache && len(c.sMapCache) > 0 {
|
||||||
|
mp, ok = c.sMapCache[key]
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rawVal, ok := c.GetValue(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeData := rawVal.(type) {
|
||||||
|
case map[string]string: // from Set
|
||||||
|
mp = typeData
|
||||||
|
case map[string]any: // decode from json,toml,yaml.v3
|
||||||
|
mp = make(map[string]string, len(typeData))
|
||||||
|
|
||||||
|
for k, v := range typeData {
|
||||||
|
switch tv := v.(type) {
|
||||||
|
case string:
|
||||||
|
if c.opts.ParseEnv {
|
||||||
|
mp[k] = envutil.ParseEnvValue(tv)
|
||||||
|
} else {
|
||||||
|
mp[k] = tv
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
mp[k] = strutil.QuietString(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case map[any]any: // decode from yaml v2
|
||||||
|
mp = make(map[string]string, len(typeData))
|
||||||
|
|
||||||
|
for k, v := range typeData {
|
||||||
|
sk := strutil.QuietString(k)
|
||||||
|
|
||||||
|
switch typVal := v.(type) {
|
||||||
|
case string:
|
||||||
|
if c.opts.ParseEnv {
|
||||||
|
mp[sk] = envutil.ParseEnvValue(typVal)
|
||||||
|
} else {
|
||||||
|
mp[sk] = typVal
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
mp[sk] = strutil.QuietString(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
c.addErrorf("value cannot be convert to map[string]string, key is %q", key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// add cache
|
||||||
|
if c.opts.EnableCache {
|
||||||
|
if c.sMapCache == nil {
|
||||||
|
c.sMapCache = make(map[string]strMap)
|
||||||
|
}
|
||||||
|
c.sMapCache[key] = mp
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubDataMap get sub config data as maputil.Map
|
||||||
|
func SubDataMap(key string) maputil.Map { return dc.SubDataMap(key) }
|
||||||
|
|
||||||
|
// SubDataMap get sub config data as maputil.Map
|
||||||
|
//
|
||||||
|
// TIP: will not enable parse Env and more
|
||||||
|
func (c *Config) SubDataMap(key string) maputil.Map {
|
||||||
|
if mp, ok := c.GetValue(key); ok {
|
||||||
|
if mmp, ok := mp.(map[string]any); ok {
|
||||||
|
return mmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep is not nil
|
||||||
|
return maputil.Map{}
|
||||||
|
}
|
||||||
+501
@@ -0,0 +1,501 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gookit/goutil/testutil"
|
||||||
|
"github.com/gookit/goutil/testutil/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig_GetValue(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
err := LoadStrings(JSON, jsonStr)
|
||||||
|
is.Nil(err)
|
||||||
|
|
||||||
|
c := Default()
|
||||||
|
|
||||||
|
// error on get
|
||||||
|
_, ok := GetValue("")
|
||||||
|
is.False(ok)
|
||||||
|
|
||||||
|
_, ok = c.GetValue("notExist")
|
||||||
|
is.False(ok)
|
||||||
|
_, ok = c.GetValue("name.sub")
|
||||||
|
is.False(ok)
|
||||||
|
is.Err(c.Error())
|
||||||
|
|
||||||
|
_, ok = c.GetValue("map1.key", false)
|
||||||
|
is.False(ok)
|
||||||
|
is.False(Exists("map1.key", false))
|
||||||
|
|
||||||
|
val, ok := GetValue("map1.notExist")
|
||||||
|
is.Nil(val)
|
||||||
|
is.False(ok)
|
||||||
|
is.False(Exists("map1.notExist"))
|
||||||
|
|
||||||
|
val, ok = GetValue("notExist.sub")
|
||||||
|
is.False(ok)
|
||||||
|
is.Nil(val)
|
||||||
|
is.False(Exists("notExist.sub"))
|
||||||
|
|
||||||
|
val, ok = c.GetValue("arr1.100")
|
||||||
|
is.Nil(val)
|
||||||
|
is.False(ok)
|
||||||
|
is.False(Exists("arr1.100"))
|
||||||
|
|
||||||
|
val, ok = c.GetValue("arr1.notExist")
|
||||||
|
is.Nil(val)
|
||||||
|
is.False(ok)
|
||||||
|
is.False(Exists("arr1.notExist"))
|
||||||
|
|
||||||
|
// load data for tests
|
||||||
|
err = c.LoadData(map[string]any{
|
||||||
|
"setStrMap": map[string]string{
|
||||||
|
"k": "v",
|
||||||
|
},
|
||||||
|
"setIntMap": map[string]int{
|
||||||
|
"k2": 23,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
is.Nil(err)
|
||||||
|
// -- assert map[string]string
|
||||||
|
is.True(Exists("setStrMap.k"))
|
||||||
|
is.Eq("v", Get("setStrMap.k"))
|
||||||
|
is.False(Exists("setStrMap.k1"))
|
||||||
|
|
||||||
|
// -- assert map[string]int
|
||||||
|
is.True(Exists("setIntMap.k2"))
|
||||||
|
is.Eq(23, Get("setIntMap.k2"))
|
||||||
|
is.False(Exists("setIntMap.k1"))
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGet(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
err := LoadStrings(JSON, jsonStr)
|
||||||
|
is.Nil(err)
|
||||||
|
|
||||||
|
// fmt.Printf("%#v\n", Data())
|
||||||
|
c := Default()
|
||||||
|
|
||||||
|
is.False(c.IsEmpty())
|
||||||
|
is.True(Exists("age"))
|
||||||
|
is.True(Exists("map1.key"))
|
||||||
|
is.True(Exists("arr1.1"))
|
||||||
|
is.False(Exists("arr1.1", false))
|
||||||
|
is.False(Exists("not-exist.sub"))
|
||||||
|
is.False(Exists(""))
|
||||||
|
is.False(Exists("not-exist"))
|
||||||
|
|
||||||
|
// get value
|
||||||
|
val := Get("age")
|
||||||
|
is.Eq(float64(123), val)
|
||||||
|
is.Eq("float64", fmt.Sprintf("%T", val))
|
||||||
|
|
||||||
|
val = Get("not-exist")
|
||||||
|
is.Nil(val)
|
||||||
|
|
||||||
|
val = Get("name")
|
||||||
|
is.Eq("app", val)
|
||||||
|
|
||||||
|
is.Eq([]string{"php", "go"}, StringsBySplit("tagsStr", ","))
|
||||||
|
|
||||||
|
// get string array
|
||||||
|
arr := Strings("notExist")
|
||||||
|
is.Empty(arr)
|
||||||
|
|
||||||
|
arr = Strings("map1")
|
||||||
|
is.Empty(arr)
|
||||||
|
|
||||||
|
arr = Strings("arr1")
|
||||||
|
is.Eq(`[]string{"val", "val1", "val2"}`, fmt.Sprintf("%#v", arr))
|
||||||
|
|
||||||
|
val = String("arr1.1")
|
||||||
|
is.Eq("val1", val)
|
||||||
|
|
||||||
|
err = LoadStrings(JSON, `{
|
||||||
|
"iArr": [12, 34, 36],
|
||||||
|
"iMap": {"k1": 12, "k2": 34, "k3": 36}
|
||||||
|
}`)
|
||||||
|
is.Nil(err)
|
||||||
|
|
||||||
|
// Ints: get int arr
|
||||||
|
iarr := Ints("name")
|
||||||
|
is.False(Exists("name.1"))
|
||||||
|
is.Empty(iarr)
|
||||||
|
|
||||||
|
iarr = Ints("notExist")
|
||||||
|
is.Empty(iarr)
|
||||||
|
|
||||||
|
iarr = Ints("iArr")
|
||||||
|
is.Eq(`[]int{12, 34, 36}`, fmt.Sprintf("%#v", iarr))
|
||||||
|
|
||||||
|
iv := Int("iArr.1")
|
||||||
|
is.Eq(34, iv)
|
||||||
|
|
||||||
|
iv = Int("iArr.100")
|
||||||
|
is.Eq(0, iv)
|
||||||
|
|
||||||
|
// IntMap: get int map
|
||||||
|
imp := IntMap("name")
|
||||||
|
is.Empty(imp)
|
||||||
|
imp = IntMap("notExist")
|
||||||
|
is.Empty(imp)
|
||||||
|
|
||||||
|
imp = IntMap("iMap")
|
||||||
|
is.NotEmpty(imp)
|
||||||
|
|
||||||
|
iv = Int("iMap.k2")
|
||||||
|
is.Eq(34, iv)
|
||||||
|
is.True(Exists("iMap.k2"))
|
||||||
|
|
||||||
|
iv = Int("iMap.notExist")
|
||||||
|
is.Eq(0, iv)
|
||||||
|
is.False(Exists("iMap.notExist"))
|
||||||
|
|
||||||
|
// set a intMap
|
||||||
|
err = Set("intMap0", map[string]int{"a": 1, "b": 2})
|
||||||
|
is.Nil(err)
|
||||||
|
|
||||||
|
imp = IntMap("intMap0")
|
||||||
|
is.NotEmpty(imp)
|
||||||
|
is.Eq(1, imp["a"])
|
||||||
|
is.Eq(2, Get("intMap0.b"))
|
||||||
|
is.True(Exists("intMap0.a"))
|
||||||
|
is.False(Exists("intMap0.c"))
|
||||||
|
|
||||||
|
// StringMap: get string map
|
||||||
|
smp := StringMap("map1")
|
||||||
|
is.Eq("val1", smp["key1"])
|
||||||
|
|
||||||
|
// like load from yaml content
|
||||||
|
// c = New("test")
|
||||||
|
err = c.LoadData(map[string]any{
|
||||||
|
"newIArr": []int{2, 3},
|
||||||
|
"newSArr": []string{"a", "b"},
|
||||||
|
"newIArr1": []any{12, 23},
|
||||||
|
"newIArr2": []any{12, "abc"},
|
||||||
|
"invalidMap": map[string]int{"k": 1},
|
||||||
|
"yMap": map[any]any{
|
||||||
|
"k0": "v0",
|
||||||
|
"k1": 23,
|
||||||
|
},
|
||||||
|
"yMap1": map[any]any{
|
||||||
|
"k": "v",
|
||||||
|
"k1": 23,
|
||||||
|
"k2": []any{23, 45},
|
||||||
|
},
|
||||||
|
"yMap10": map[string]any{
|
||||||
|
"k": "v",
|
||||||
|
"k1": 23,
|
||||||
|
"k2": []any{23, 45},
|
||||||
|
},
|
||||||
|
"yMap2": map[any]any{
|
||||||
|
"k": 2,
|
||||||
|
"k1": 23,
|
||||||
|
},
|
||||||
|
"yArr": []any{23, 45, "val", map[string]any{"k4": "v4"}},
|
||||||
|
})
|
||||||
|
is.Nil(err)
|
||||||
|
|
||||||
|
iarr = Ints("newIArr")
|
||||||
|
is.Eq("[2 3]", fmt.Sprintf("%v", iarr))
|
||||||
|
|
||||||
|
iarr = Ints("newIArr1")
|
||||||
|
is.Eq("[12 23]", fmt.Sprintf("%v", iarr))
|
||||||
|
iarr = Ints("newIArr2")
|
||||||
|
is.Empty(iarr)
|
||||||
|
|
||||||
|
iv = Int("newIArr.1")
|
||||||
|
is.True(Exists("newIArr.1"))
|
||||||
|
is.Eq(3, iv)
|
||||||
|
|
||||||
|
iv = Int("newIArr.200")
|
||||||
|
is.False(Exists("newIArr.200"))
|
||||||
|
is.Eq(0, iv)
|
||||||
|
|
||||||
|
// invalid intMap
|
||||||
|
imp = IntMap("yMap1")
|
||||||
|
is.Empty(imp)
|
||||||
|
|
||||||
|
imp = IntMap("yMap10")
|
||||||
|
is.Empty(imp)
|
||||||
|
|
||||||
|
imp = IntMap("yMap2")
|
||||||
|
is.Eq(2, imp["k"])
|
||||||
|
|
||||||
|
val = String("newSArr.1")
|
||||||
|
is.True(Exists("newSArr.1"))
|
||||||
|
is.Eq("b", val)
|
||||||
|
|
||||||
|
val = String("newSArr.100")
|
||||||
|
is.False(Exists("newSArr.100"))
|
||||||
|
is.Eq("", val)
|
||||||
|
|
||||||
|
smp = StringMap("invalidMap")
|
||||||
|
is.Nil(smp)
|
||||||
|
|
||||||
|
smp = StringMap("yMap.notExist")
|
||||||
|
is.Nil(smp)
|
||||||
|
|
||||||
|
smp = StringMap("yMap")
|
||||||
|
is.True(Exists("yMap.k0"))
|
||||||
|
is.False(Exists("yMap.k100"))
|
||||||
|
is.Eq("v0", smp["k0"])
|
||||||
|
|
||||||
|
iarr = Ints("yMap1.k2")
|
||||||
|
is.Eq("[23 45]", fmt.Sprintf("%v", iarr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInt(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
ClearAll()
|
||||||
|
_ = LoadStrings(JSON, jsonStr)
|
||||||
|
|
||||||
|
is.True(Exists("age"))
|
||||||
|
|
||||||
|
iv := Int("age")
|
||||||
|
is.Eq(123, iv)
|
||||||
|
|
||||||
|
iv = Int("name")
|
||||||
|
is.Eq(0, iv)
|
||||||
|
|
||||||
|
iv = Int("notExist", 34)
|
||||||
|
is.Eq(34, iv)
|
||||||
|
|
||||||
|
c := Default()
|
||||||
|
iv = c.Int("age")
|
||||||
|
is.Eq(123, iv)
|
||||||
|
iv = c.Int("notExist")
|
||||||
|
is.Eq(0, iv)
|
||||||
|
|
||||||
|
uiv := Uint("age")
|
||||||
|
is.Eq(uint(123), uiv)
|
||||||
|
|
||||||
|
dur := Duration("age")
|
||||||
|
is.Eq(time.Duration(123), dur)
|
||||||
|
is.Eq(time.Duration(340), Duration("not-exist", 340))
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInt64(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
ClearAll()
|
||||||
|
_ = LoadStrings(JSON, jsonStr)
|
||||||
|
|
||||||
|
// get int64
|
||||||
|
iv64 := Int64("age")
|
||||||
|
is.Eq(int64(123), iv64)
|
||||||
|
|
||||||
|
iv64 = Int64("name")
|
||||||
|
is.Eq(iv64, int64(0))
|
||||||
|
|
||||||
|
iv64 = Int64("age", 34)
|
||||||
|
is.Eq(int64(123), iv64)
|
||||||
|
iv64 = Int64("notExist", 34)
|
||||||
|
is.Eq(int64(34), iv64)
|
||||||
|
|
||||||
|
c := Default()
|
||||||
|
iv64 = c.Int64("age")
|
||||||
|
is.Eq(int64(123), iv64)
|
||||||
|
iv64 = c.Int64("notExist")
|
||||||
|
is.Eq(int64(0), iv64)
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFloat(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
ClearAll()
|
||||||
|
_ = LoadStrings(JSON, jsonStr)
|
||||||
|
c := Default()
|
||||||
|
|
||||||
|
// get float
|
||||||
|
err := c.Set("flVal", 23.45)
|
||||||
|
is.Nil(err)
|
||||||
|
flt := c.Float("flVal")
|
||||||
|
is.Eq(23.45, flt)
|
||||||
|
|
||||||
|
flt = Float("name")
|
||||||
|
is.Eq(float64(0), flt)
|
||||||
|
|
||||||
|
flt = c.Float("notExists")
|
||||||
|
is.Eq(float64(0), flt)
|
||||||
|
|
||||||
|
flt = c.Float("notExists", 10)
|
||||||
|
is.Eq(float64(10), flt)
|
||||||
|
|
||||||
|
flt = Float("flVal", 0)
|
||||||
|
is.Eq(23.45, flt)
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestString(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
ClearAll()
|
||||||
|
_ = LoadStrings(JSON, jsonStr)
|
||||||
|
|
||||||
|
// get string
|
||||||
|
val := String("arr1")
|
||||||
|
is.Eq("[val val1 val2]", val)
|
||||||
|
|
||||||
|
str := String("notExists")
|
||||||
|
is.Eq("", str)
|
||||||
|
is.Panics(func() {
|
||||||
|
MustString("notExists")
|
||||||
|
})
|
||||||
|
|
||||||
|
str = String("notExists", "defVal")
|
||||||
|
is.Eq("defVal", str)
|
||||||
|
|
||||||
|
c := Default()
|
||||||
|
str = c.String("name")
|
||||||
|
is.Eq("app", str)
|
||||||
|
str = c.String("notExist")
|
||||||
|
is.Eq("", str)
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBool(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
ClearAll()
|
||||||
|
_ = LoadSources(JSON, []byte(jsonStr))
|
||||||
|
|
||||||
|
// get bool
|
||||||
|
val := Get("debug")
|
||||||
|
is.Eq(true, val)
|
||||||
|
|
||||||
|
bv := Bool("debug")
|
||||||
|
is.Eq(true, bv)
|
||||||
|
|
||||||
|
bv = Bool("age")
|
||||||
|
is.Eq(false, bv)
|
||||||
|
|
||||||
|
bv = Bool("debug", false)
|
||||||
|
is.Eq(true, bv)
|
||||||
|
|
||||||
|
bv = Bool("notExist", false)
|
||||||
|
is.Eq(false, bv)
|
||||||
|
|
||||||
|
c := Default()
|
||||||
|
bv = c.Bool("debug")
|
||||||
|
is.True(bv)
|
||||||
|
bv = c.Bool("notExist")
|
||||||
|
is.False(bv)
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubDataMap(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
ClearAll()
|
||||||
|
_ = LoadSources(JSON, []byte(jsonStr))
|
||||||
|
|
||||||
|
mp := SubDataMap("map1")
|
||||||
|
is.NotEmpty(mp)
|
||||||
|
is.Eq("230", mp.Get("key4"))
|
||||||
|
is.Eq(230, mp.Int("key4"))
|
||||||
|
|
||||||
|
mp = SubDataMap("notExist")
|
||||||
|
is.Empty(mp)
|
||||||
|
|
||||||
|
ClearAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseEnv(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
|
||||||
|
cfg := NewWithOptions("test", ParseEnv)
|
||||||
|
err := cfg.LoadStrings(JSON, `{
|
||||||
|
"ekey": "${EnvKey}",
|
||||||
|
"ekey0": "${ EnvKey0 }",
|
||||||
|
"ekey1": "${EnvKey1|defValue}",
|
||||||
|
"ekey2": "${ EnvKey2 | defValue1 }",
|
||||||
|
"ekey3": "${ EnvKey3 | app:run }",
|
||||||
|
"ekey4": "${FirstEnv}/${ SecondEnv }",
|
||||||
|
"ekey5": "${TEST_SHELL|/bin/bash}",
|
||||||
|
"ekey6": "${ EnvKey6 | app=run }",
|
||||||
|
"ekey7": "${ EnvKey7 | app.run }",
|
||||||
|
"ekey8": "${ EnvKey8 | app/run }"
|
||||||
|
}`)
|
||||||
|
|
||||||
|
is.NoErr(err)
|
||||||
|
|
||||||
|
tests := []struct{ EKey, EVal, CKey, CVal string }{
|
||||||
|
{"EnvKey", "EnvKey val", "ekey", "EnvKey val"},
|
||||||
|
{"EnvKey", "", "ekey", ""},
|
||||||
|
{"EnvKey0", "EnvKey0 val", "ekey0", "EnvKey0 val"},
|
||||||
|
{"EnvKey3", "EnvKey3 val", "ekey3", "EnvKey3 val"},
|
||||||
|
{"EnvKey3", "", "ekey3", "app:run"},
|
||||||
|
{"EnvKey6", "", "ekey6", "app=run"},
|
||||||
|
{"EnvKey7", "", "ekey7", "app.run"},
|
||||||
|
{"EnvKey8", "", "ekey8", "app/run"},
|
||||||
|
{"TEST_SHELL", "/bin/zsh", "ekey5", "/bin/zsh"},
|
||||||
|
{"TEST_SHELL", "", "ekey5", "/bin/bash"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, smp := range tests {
|
||||||
|
is.Eq("", Getenv(smp.EKey))
|
||||||
|
|
||||||
|
testutil.MockEnvValue(smp.EKey, smp.EVal, func(eVal string) {
|
||||||
|
is.Eq(smp.EVal, eVal)
|
||||||
|
is.Eq(smp.CVal, cfg.String(smp.CKey))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// test multi ENV key
|
||||||
|
is.Eq("", Getenv("FirstEnv"))
|
||||||
|
|
||||||
|
testutil.MockEnvValues(map[string]string{
|
||||||
|
"FirstEnv": "abc",
|
||||||
|
"SecondEnv": "def",
|
||||||
|
}, func() {
|
||||||
|
is.Eq("abc", Getenv("FirstEnv"))
|
||||||
|
is.Eq("def", Getenv("SecondEnv"))
|
||||||
|
is.Eq("abc/def", cfg.String("ekey4"))
|
||||||
|
})
|
||||||
|
|
||||||
|
testutil.MockEnvValues(map[string]string{
|
||||||
|
"FirstEnv": "abc",
|
||||||
|
}, func() {
|
||||||
|
is.Eq("abc", Getenv("FirstEnv"))
|
||||||
|
is.Eq("", Getenv("SecondEnv"))
|
||||||
|
is.Eq("abc/", cfg.String("ekey4"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_Duration_getter(t *testing.T) {
|
||||||
|
is := assert.New(t)
|
||||||
|
c := New("test")
|
||||||
|
err := c.LoadStrings(JSON, `{
|
||||||
|
"ttl": "300s",
|
||||||
|
"idle": "1h30m",
|
||||||
|
"short": "20m",
|
||||||
|
"nanos": 500,
|
||||||
|
"bad": "not-a-duration"
|
||||||
|
}`)
|
||||||
|
is.Nil(err)
|
||||||
|
|
||||||
|
// Go duration strings are parsed.
|
||||||
|
is.Eq(300*time.Second, c.Duration("ttl"))
|
||||||
|
is.Eq(90*time.Minute, c.Duration("idle"))
|
||||||
|
is.Eq(20*time.Minute, c.Duration("short"))
|
||||||
|
|
||||||
|
// A bare integer stays nanoseconds (backwards compatible).
|
||||||
|
is.Eq(time.Duration(500), c.Duration("nanos"))
|
||||||
|
|
||||||
|
// Unparseable and missing fall back to the default.
|
||||||
|
is.Eq(5*time.Minute, c.Duration("bad", 5*time.Minute))
|
||||||
|
is.Eq(7*time.Second, c.Duration("missing", 7*time.Second))
|
||||||
|
is.Eq(time.Duration(0), c.Duration("missing"))
|
||||||
|
}
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
; exported at 2025-08-20 14:11:56
|
||||||
|
|
||||||
|
age = 123
|
||||||
|
baseKey = value
|
||||||
|
debug = false
|
||||||
|
envKey = ${SHELL}
|
||||||
|
envKey1 = ${NotExist|defValue}
|
||||||
|
multiWords = hello world
|
||||||
|
name = app
|
||||||
|
|
||||||
|
[map1]
|
||||||
|
key1 = val1
|
||||||
|
key2 = val2
|
||||||
|
key = val
|
||||||
|
|
||||||
|
|
||||||
Reference in new issue
Block a user