159 lines
3.8 KiB
Go
159 lines
3.8 KiB
Go
|
|
package runtime
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"reflect"
|
||
|
|
"strconv"
|
||
|
|
"sync/atomic"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
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(any) error
|
||
|
|
Load() any
|
||
|
|
Store(any)
|
||
|
|
}
|
||
|
|
|
||
|
|
type atomicValue struct {
|
||
|
|
atomic.Value
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) typeAssertError() error {
|
||
|
|
return fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) Bool() (bool, error) {
|
||
|
|
switch value := v.Load().(type) {
|
||
|
|
case bool:
|
||
|
|
return value, nil
|
||
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||
|
|
return strconv.ParseBool(fmt.Sprint(value))
|
||
|
|
case string:
|
||
|
|
return strconv.ParseBool(value)
|
||
|
|
}
|
||
|
|
return false, v.typeAssertError()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) Int() (int64, error) {
|
||
|
|
switch value := v.Load().(type) {
|
||
|
|
case int:
|
||
|
|
return int64(value), nil
|
||
|
|
case int8:
|
||
|
|
return int64(value), nil
|
||
|
|
case int16:
|
||
|
|
return int64(value), nil
|
||
|
|
case int32:
|
||
|
|
return int64(value), nil
|
||
|
|
case int64:
|
||
|
|
return value, nil
|
||
|
|
case uint:
|
||
|
|
return int64(value), nil
|
||
|
|
case uint8:
|
||
|
|
return int64(value), nil
|
||
|
|
case uint16:
|
||
|
|
return int64(value), nil
|
||
|
|
case uint32:
|
||
|
|
return int64(value), nil
|
||
|
|
case uint64:
|
||
|
|
return int64(value), nil
|
||
|
|
case float32:
|
||
|
|
return int64(value), nil
|
||
|
|
case float64:
|
||
|
|
return int64(value), nil
|
||
|
|
case string:
|
||
|
|
return strconv.ParseInt(value, 10, 64)
|
||
|
|
}
|
||
|
|
return 0, v.typeAssertError()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) Float() (float64, error) {
|
||
|
|
switch value := v.Load().(type) {
|
||
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||
|
|
return strconv.ParseFloat(fmt.Sprint(value), 64)
|
||
|
|
case string:
|
||
|
|
return strconv.ParseFloat(value, 64)
|
||
|
|
}
|
||
|
|
return 0, v.typeAssertError()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) String() (string, error) {
|
||
|
|
switch value := v.Load().(type) {
|
||
|
|
case string:
|
||
|
|
return value, nil
|
||
|
|
case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||
|
|
return fmt.Sprint(value), nil
|
||
|
|
case []byte:
|
||
|
|
return string(value), nil
|
||
|
|
case fmt.Stringer:
|
||
|
|
return value.String(), nil
|
||
|
|
}
|
||
|
|
return "", v.typeAssertError()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) Duration() (time.Duration, error) {
|
||
|
|
value, err := v.Int()
|
||
|
|
if err != nil {
|
||
|
|
return 0, err
|
||
|
|
}
|
||
|
|
return time.Duration(value), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) Slice() ([]Value, error) {
|
||
|
|
values, ok := v.Load().([]any)
|
||
|
|
if !ok {
|
||
|
|
return nil, v.typeAssertError()
|
||
|
|
}
|
||
|
|
out := make([]Value, 0, len(values))
|
||
|
|
for _, value := range values {
|
||
|
|
item := &atomicValue{}
|
||
|
|
item.Store(value)
|
||
|
|
out = append(out, item)
|
||
|
|
}
|
||
|
|
return out, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) Map() (map[string]Value, error) {
|
||
|
|
values, ok := v.Load().(map[string]any)
|
||
|
|
if !ok {
|
||
|
|
return nil, v.typeAssertError()
|
||
|
|
}
|
||
|
|
out := make(map[string]Value, len(values))
|
||
|
|
for key, value := range values {
|
||
|
|
item := &atomicValue{}
|
||
|
|
item.Store(value)
|
||
|
|
out[key] = item
|
||
|
|
}
|
||
|
|
return out, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (v *atomicValue) Scan(obj any) error {
|
||
|
|
data, err := json.Marshal(v.Load())
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
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, 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(any) error { return v.err }
|
||
|
|
func (v errValue) Load() any { return nil }
|
||
|
|
func (v errValue) Store(any) {}
|
||
|
|
func (v errValue) Slice() ([]Value, error) { return nil, v.err }
|
||
|
|
func (v errValue) Map() (map[string]Value, error) { return nil, v.err }
|