Path: blob/main/pkg/river/internal/reflectutil/walk.go
4096 views
package reflectutil12import (3"reflect"45"github.com/grafana/agent/pkg/river/internal/rivertags"6)78// GetOrAlloc returns the nested field of value corresponding to index.9// GetOrAlloc panics if not given a struct.10func GetOrAlloc(value reflect.Value, field rivertags.Field) reflect.Value {11return GetOrAllocIndex(value, field.Index)12}1314// GetOrAllocIndex returns the nested field of value corresponding to index.15// GetOrAllocIndex panics if not given a struct.16//17// It is similar to [reflect/Value.FieldByIndex] but can handle traversing18// through nil pointers. If allocate is true, GetOrAllocIndex allocates any19// intermediate nil pointers while traversing the struct.20func GetOrAllocIndex(value reflect.Value, index []int) reflect.Value {21if len(index) == 1 {22return value.Field(index[0])23}2425if value.Kind() != reflect.Struct {26panic("GetOrAlloc must be given a Struct, but found " + value.Kind().String())27}2829for _, next := range index {30value = deferencePointer(value).Field(next)31}3233return value34}3536func deferencePointer(value reflect.Value) reflect.Value {37for value.Kind() == reflect.Pointer {38if value.IsNil() {39value.Set(reflect.New(value.Type().Elem()))40}41value = value.Elem()42}4344return value45}4647// Get returns the nested field of value corresponding to index. Get panics if48// not given a struct.49//50// It is similar to [reflect/Value.FieldByIndex] but can handle traversing51// through nil pointers. If Get traverses through a nil pointer, a non-settable52// zero value for the final field is returned.53func Get(value reflect.Value, field rivertags.Field) reflect.Value {54if len(field.Index) == 1 {55return value.Field(field.Index[0])56}5758if value.Kind() != reflect.Struct {59panic("Get must be given a Struct, but found " + value.Kind().String())60}6162for i, next := range field.Index {63for value.Kind() == reflect.Pointer {64if value.IsNil() {65return getZero(value, field.Index[i:])66}67value = value.Elem()68}6970value = value.Field(next)71}7273return value74}7576// getZero returns a non-settable zero value while walking value.77func getZero(value reflect.Value, index []int) reflect.Value {78typ := value.Type()7980for _, next := range index {81for typ.Kind() == reflect.Pointer {82typ = typ.Elem()83}84typ = typ.Field(next).Type85}8687return reflect.Zero(typ)88}899091