package value
import (
"encoding"
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/grafana/agent/pkg/river/internal/reflectutil"
)
var (
goAny = reflect.TypeOf((*interface{})(nil)).Elem()
goString = reflect.TypeOf(string(""))
goByteSlice = reflect.TypeOf([]byte(nil))
goError = reflect.TypeOf((*error)(nil)).Elem()
goTextMarshaler = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
goTextUnmarshaler = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
goStructWrapper = reflect.TypeOf(structWrapper{})
goCapsule = reflect.TypeOf((*Capsule)(nil)).Elem()
goDuration = reflect.TypeOf((time.Duration)(0))
goDurationPtr = reflect.TypeOf((*time.Duration)(nil))
goRiverDecoder = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
goRawRiverFunc = reflect.TypeOf((RawFunction)(nil))
goRiverValue = reflect.TypeOf(Null)
)
type Value struct {
rv reflect.Value
ty Type
}
var Null = Value{}
func Uint(u uint64) Value { return Value{rv: reflect.ValueOf(u), ty: TypeNumber} }
func Int(i int64) Value { return Value{rv: reflect.ValueOf(i), ty: TypeNumber} }
func Float(f float64) Value { return Value{rv: reflect.ValueOf(f), ty: TypeNumber} }
func String(s string) Value { return Value{rv: reflect.ValueOf(s), ty: TypeString} }
func Bool(b bool) Value { return Value{rv: reflect.ValueOf(b), ty: TypeBool} }
func Object(m map[string]Value) Value {
return Value{
rv: reflect.ValueOf(m),
ty: TypeObject,
}
}
func Array(vv ...Value) Value {
return Value{
rv: reflect.ValueOf(vv),
ty: TypeArray,
}
}
func Func(f interface{}) Value {
rv := reflect.ValueOf(f)
if RiverType(rv.Type()) != TypeFunction {
panic("river/value: Func called with non-function type")
}
return Value{rv: rv, ty: TypeFunction}
}
func Encapsulate(v interface{}) Value {
rv := reflect.ValueOf(v)
if RiverType(rv.Type()) != TypeCapsule {
panic("river/value: Capsule called with non-capsule type")
}
return Value{rv: rv, ty: TypeCapsule}
}
func Encode(v interface{}) Value {
if v == nil {
return Null
}
return makeValue(reflect.ValueOf(v))
}
func FromRaw(v reflect.Value) Value {
return makeValue(v)
}
func (v Value) Type() Type { return v.ty }
func (v Value) Describe() string {
if v.ty != TypeCapsule {
return v.ty.String()
}
return fmt.Sprintf("capsule(%q)", v.rv.Type())
}
func (v Value) Bool() bool {
if v.ty != TypeBool {
panic("river/value: Bool called on non-bool type")
}
return v.rv.Bool()
}
func (v Value) Number() Number {
if v.ty != TypeNumber {
panic("river/value: Number called on non-number type")
}
return newNumberValue(v.rv)
}
func (v Value) Int() int64 {
if v.ty != TypeNumber {
panic("river/value: Int called on non-number type")
}
switch makeNumberKind(v.rv.Kind()) {
case NumberKindInt:
return v.rv.Int()
case NumberKindUint:
return int64(v.rv.Uint())
case NumberKindFloat:
return int64(v.rv.Float())
}
panic("river/value: unreachable")
}
func (v Value) Uint() uint64 {
if v.ty != TypeNumber {
panic("river/value: Uint called on non-number type")
}
switch makeNumberKind(v.rv.Kind()) {
case NumberKindInt:
return uint64(v.rv.Int())
case NumberKindUint:
return v.rv.Uint()
case NumberKindFloat:
return uint64(v.rv.Float())
}
panic("river/value: unreachable")
}
func (v Value) Float() float64 {
if v.ty != TypeNumber {
panic("river/value: Float called on non-number type")
}
switch makeNumberKind(v.rv.Kind()) {
case NumberKindInt:
return float64(v.rv.Int())
case NumberKindUint:
return float64(v.rv.Uint())
case NumberKindFloat:
return v.rv.Float()
}
panic("river/value: unreachable")
}
func (v Value) Text() string {
if v.ty != TypeString {
panic("river/value: Text called on non-string type")
}
addrRV := v.rv
if addrRV.CanAddr() {
addrRV = addrRV.Addr()
}
switch {
case addrRV.Type().Implements(goTextMarshaler):
text, _ := addrRV.Interface().(encoding.TextMarshaler).MarshalText()
return string(text)
case v.rv.Type() == goDuration:
return v.rv.Interface().(time.Duration).String()
default:
return v.rv.String()
}
}
func (v Value) Len() int {
switch v.ty {
case TypeArray:
return v.rv.Len()
case TypeObject:
switch {
case v.rv.Type() == goStructWrapper:
return v.rv.Interface().(structWrapper).Len()
case v.rv.Kind() == reflect.Array, v.rv.Kind() == reflect.Slice:
return v.rv.Len()
case v.rv.Kind() == reflect.Struct:
return getCachedTags(v.rv.Type()).Len()
case v.rv.Kind() == reflect.Map:
return v.rv.Len()
}
}
panic("river/value: Len called on non-array and non-object value")
}
func (v Value) Index(i int) Value {
if v.ty != TypeArray {
panic("river/value: Index called on non-array value")
}
return makeValue(v.rv.Index(i))
}
func (v Value) Interface() interface{} {
if v.ty == TypeNull {
return nil
}
return v.rv.Interface()
}
func (v Value) Reflect() reflect.Value { return v.rv }
func makeValue(v reflect.Value) Value {
if v.IsValid() && v.Type() == goAny {
v = v.Elem()
}
if v.IsValid() && v.Type() == goRiverValue {
v = v.Interface().(Value).rv
}
if v.CanAddr() {
v = v.Addr()
}
if !v.IsValid() {
return Null
}
riverType := RiverType(v.Type())
for v.Kind() == reflect.Pointer {
if v.IsNil() {
return Null
}
v = v.Elem()
}
return Value{rv: v, ty: riverType}
}
func (v Value) OrderedKeys() bool {
if v.ty != TypeObject {
panic("river/value: OrderedKeys called on non-object value")
}
return v.rv.Kind() != reflect.Map
}
func (v Value) Keys() []string {
if v.ty != TypeObject {
panic("river/value: Keys called on non-object value")
}
switch {
case v.rv.Type() == goStructWrapper:
return v.rv.Interface().(structWrapper).Keys()
case v.rv.Kind() == reflect.Struct:
return wrapStruct(v.rv, true).Keys()
case v.rv.Kind() == reflect.Array, v.rv.Kind() == reflect.Slice:
labelField, _ := getCachedTags(v.rv.Type().Elem()).LabelField()
keys := make([]string, v.rv.Len())
for i := range keys {
keys[i] = reflectutil.Get(v.rv.Index(i), labelField).String()
}
return keys
case v.rv.Kind() == reflect.Map:
reflectKeys := v.rv.MapKeys()
res := make([]string, len(reflectKeys))
for i, rk := range reflectKeys {
res[i] = rk.String()
}
return res
}
panic("river/value: unreachable")
}
func (v Value) Key(key string) (index Value, ok bool) {
if v.ty != TypeObject {
panic("river/value: Key called on non-object value")
}
switch {
case v.rv.Type() == goStructWrapper:
return v.rv.Interface().(structWrapper).Key(key)
case v.rv.Kind() == reflect.Struct:
return wrapStruct(v.rv, true).Key(key)
case v.rv.Kind() == reflect.Map:
val := v.rv.MapIndex(reflect.ValueOf(key))
if !val.IsValid() {
return Null, false
}
return makeValue(val), true
case v.rv.Kind() == reflect.Slice, v.rv.Kind() == reflect.Array:
labelField, _ := getCachedTags(v.rv.Type().Elem()).LabelField()
for i := 0; i < v.rv.Len(); i++ {
elem := v.rv.Index(i)
label := reflectutil.Get(elem, labelField).String()
if label == key {
ws := wrapStruct(elem, false)
return ws.Value(), true
}
}
default:
panic("river/value: unreachable")
}
return
}
func (v Value) Call(args ...Value) (Value, error) {
if v.ty != TypeFunction {
panic("river/value: Call called on non-function type")
}
if v.rv.Type() == goRawRiverFunc {
return v.rv.Interface().(RawFunction)(v, args...)
}
var (
variadic = v.rv.Type().IsVariadic()
expectedArgs = v.rv.Type().NumIn()
)
if variadic && len(args) < expectedArgs-1 {
return Null, Error{
Value: v,
Inner: fmt.Errorf("expected at least %d args, got %d", expectedArgs-1, len(args)),
}
} else if !variadic && len(args) != expectedArgs {
return Null, Error{
Value: v,
Inner: fmt.Errorf("expected %d args, got %d", expectedArgs, len(args)),
}
}
reflectArgs := make([]reflect.Value, len(args))
for i, arg := range args {
var argVal reflect.Value
if variadic && i >= expectedArgs-1 {
argType := v.rv.Type().In(expectedArgs - 1).Elem()
argVal = reflect.New(argType).Elem()
} else {
argType := v.rv.Type().In(i)
argVal = reflect.New(argType).Elem()
}
var d decoder
if err := d.decode(arg, argVal); err != nil {
return Null, ArgError{
Function: v,
Argument: arg,
Index: i,
Inner: err,
}
}
reflectArgs[i] = argVal
}
outs := v.rv.Call(reflectArgs)
switch len(outs) {
case 1:
return makeValue(outs[0]), nil
case 2:
err, _ := outs[1].Interface().(error)
if err != nil {
return Null, Error{Value: v, Inner: err}
}
return makeValue(outs[0]), nil
default:
panic("river/value: unreachable")
}
}
func convertValue(val Value, toType Type) (Value, error) {
fromType := val.Type()
if fromType == toType {
return val, nil
}
switch fromType {
case TypeNumber:
switch toType {
case TypeString:
strVal := newNumberValue(val.rv).ToString()
return makeValue(reflect.ValueOf(strVal)), nil
}
case TypeString:
sourceStr := val.rv.String()
switch toType {
case TypeNumber:
switch {
case sourceStr == "":
return Null, TypeError{Value: val, Expected: toType}
case sourceStr[0] == '-':
parsed, err := strconv.ParseInt(sourceStr, 10, 64)
if err != nil {
return Null, TypeError{Value: val, Expected: toType}
}
return Int(parsed), nil
case strings.ContainsAny(sourceStr, ".eE"):
parsed, err := strconv.ParseFloat(sourceStr, 64)
if err != nil {
return Null, TypeError{Value: val, Expected: toType}
}
return Float(parsed), nil
default:
parsed, err := strconv.ParseUint(sourceStr, 10, 64)
if err != nil {
return Null, TypeError{Value: val, Expected: toType}
}
return Uint(parsed), nil
}
}
}
return Null, TypeError{Value: val, Expected: toType}
}
func convertGoNumber(nval Number, target reflect.Type) reflect.Value {
switch target.Kind() {
case reflect.Int:
return reflect.ValueOf(int(nval.Int()))
case reflect.Int8:
return reflect.ValueOf(int8(nval.Int()))
case reflect.Int16:
return reflect.ValueOf(int16(nval.Int()))
case reflect.Int32:
return reflect.ValueOf(int32(nval.Int()))
case reflect.Int64:
return reflect.ValueOf(nval.Int())
case reflect.Uint:
return reflect.ValueOf(uint(nval.Uint()))
case reflect.Uint8:
return reflect.ValueOf(uint8(nval.Uint()))
case reflect.Uint16:
return reflect.ValueOf(uint16(nval.Uint()))
case reflect.Uint32:
return reflect.ValueOf(uint32(nval.Uint()))
case reflect.Uint64:
return reflect.ValueOf(nval.Uint())
case reflect.Float32:
return reflect.ValueOf(float32(nval.Float()))
case reflect.Float64:
return reflect.ValueOf(nval.Float())
}
panic("unsupported number conversion")
}