You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
3.7 KiB
154 lines
3.7 KiB
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"google.golang.org/protobuf/encoding/protojson"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
var (
|
|
_ Value = (*atomicValue)(nil)
|
|
_ Value = (*errValue)(nil)
|
|
)
|
|
|
|
// Value is config value interface.
|
|
type Value interface {
|
|
Bool() (bool, error)
|
|
Int() (int64, error)
|
|
Float() (float64, error)
|
|
String() (string, error)
|
|
Duration() (time.Duration, error)
|
|
Slice() ([]Value, error)
|
|
Map() (map[string]Value, error)
|
|
Scan(interface{}) error
|
|
Load() interface{}
|
|
Store(interface{})
|
|
}
|
|
|
|
type atomicValue struct {
|
|
atomic.Value
|
|
}
|
|
|
|
func (v *atomicValue) Bool() (bool, error) {
|
|
switch val := v.Load().(type) {
|
|
case bool:
|
|
return val, nil
|
|
case int, int32, int64, float64, string:
|
|
return strconv.ParseBool(fmt.Sprint(val))
|
|
}
|
|
return false, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
|
|
}
|
|
|
|
func (v *atomicValue) Int() (int64, error) {
|
|
switch val := v.Load().(type) {
|
|
case int:
|
|
return int64(val), nil
|
|
case int32:
|
|
return int64(val), nil
|
|
case int64:
|
|
return val, nil
|
|
case float64:
|
|
return int64(val), nil
|
|
case string:
|
|
return strconv.ParseInt(val, 10, 64) //nolint:gomnd
|
|
}
|
|
return 0, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
|
|
}
|
|
|
|
func (v *atomicValue) Slice() ([]Value, error) {
|
|
if vals, ok := v.Load().([]interface{}); ok {
|
|
var slices []Value
|
|
for _, val := range vals {
|
|
a := &atomicValue{}
|
|
a.Store(val)
|
|
slices = append(slices, a)
|
|
}
|
|
return slices, nil
|
|
}
|
|
return nil, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
|
|
}
|
|
|
|
func (v *atomicValue) Map() (map[string]Value, error) {
|
|
if vals, ok := v.Load().(map[string]interface{}); ok {
|
|
m := make(map[string]Value)
|
|
for key, val := range vals {
|
|
a := &atomicValue{}
|
|
a.Store(val)
|
|
m[key] = a
|
|
}
|
|
return m, nil
|
|
}
|
|
return nil, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
|
|
}
|
|
|
|
func (v *atomicValue) Float() (float64, error) {
|
|
switch val := v.Load().(type) {
|
|
case float64:
|
|
return val, nil
|
|
case int:
|
|
return float64(val), nil
|
|
case int32:
|
|
return float64(val), nil
|
|
case int64:
|
|
return float64(val), nil
|
|
case string:
|
|
return strconv.ParseFloat(val, 64) //nolint:gomnd
|
|
}
|
|
return 0.0, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
|
|
}
|
|
|
|
func (v *atomicValue) String() (string, error) {
|
|
switch val := v.Load().(type) {
|
|
case string:
|
|
return val, nil
|
|
case bool, int, int32, int64, float64:
|
|
return fmt.Sprint(val), nil
|
|
case []byte:
|
|
return string(val), nil
|
|
default:
|
|
if s, ok := val.(fmt.Stringer); ok {
|
|
return s.String(), nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
|
|
}
|
|
|
|
func (v *atomicValue) Duration() (time.Duration, error) {
|
|
val, err := v.Int()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return time.Duration(val), nil
|
|
}
|
|
|
|
func (v *atomicValue) Scan(obj interface{}) error {
|
|
data, err := json.Marshal(v.Load())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if pb, ok := obj.(proto.Message); ok {
|
|
return protojson.Unmarshal(data, pb)
|
|
}
|
|
return json.Unmarshal(data, obj)
|
|
}
|
|
|
|
type errValue struct {
|
|
err error
|
|
}
|
|
|
|
func (v errValue) Bool() (bool, error) { return false, v.err }
|
|
func (v errValue) Int() (int64, error) { return 0, v.err }
|
|
func (v errValue) Float() (float64, error) { return 0.0, v.err }
|
|
func (v errValue) Duration() (time.Duration, error) { return 0, v.err }
|
|
func (v errValue) String() (string, error) { return "", v.err }
|
|
func (v errValue) Scan(interface{}) error { return v.err }
|
|
func (v errValue) Load() interface{} { return nil }
|
|
func (v errValue) Store(interface{}) {}
|
|
func (v errValue) Slice() ([]Value, error) { return nil, v.err }
|
|
func (v errValue) Map() (map[string]Value, error) { return nil, v.err }
|
|
|