coder/testutil/reflect.go
Steven Masley 343f8ec9ab chore: join owner, template, and org in new workspace view (#15116)
Joins in fields like `username`, `avatar_url`, `organization_name`,
`template_name` to `workspaces` via a **view**. 
The view must be maintained moving forward, but this prevents needing to
add RBAC permissions to fetch related workspace fields.
2024-10-22 09:20:54 -05:00

145 lines
3.4 KiB
Go

package testutil
import (
"reflect"
"time"
"golang.org/x/xerrors"
)
type Random struct {
String func() string
Bool func() bool
Int func() int64
Uint func() uint64
Float func() float64
Complex func() complex128
Time func() time.Time
}
func NewRandom() *Random {
// Guaranteed to be random...
return &Random{
String: func() string { return "foo" },
Bool: func() bool { return true },
Int: func() int64 { return 500 },
Uint: func() uint64 { return 126 },
Float: func() float64 { return 3.14 },
Complex: func() complex128 { return 6.24 },
Time: func() time.Time { return time.Date(2020, 5, 2, 5, 19, 21, 30, time.UTC) },
}
}
// PopulateStruct does a best effort to populate a struct with random values.
func PopulateStruct(s interface{}, r *Random) error {
if r == nil {
r = NewRandom()
}
v := reflect.ValueOf(s)
if v.Kind() != reflect.Ptr || v.IsNil() {
return xerrors.Errorf("s must be a non-nil pointer")
}
v = v.Elem()
if v.Kind() != reflect.Struct {
return xerrors.Errorf("s must be a pointer to a struct")
}
t := v.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fieldName := field.Name
fieldValue := v.Field(i)
if !fieldValue.CanSet() {
continue // Skip if field is unexported
}
nv, err := populateValue(fieldValue, r)
if err != nil {
return xerrors.Errorf("%s : %w", fieldName, err)
}
v.Field(i).Set(nv)
}
return nil
}
func populateValue(v reflect.Value, r *Random) (reflect.Value, error) {
var err error
// Handle some special cases
switch v.Type() {
case reflect.TypeOf(time.Time{}):
v.Set(reflect.ValueOf(r.Time()))
return v, nil
default:
// Go to Kind instead
}
switch v.Kind() {
case reflect.Struct:
if err := PopulateStruct(v.Addr().Interface(), r); err != nil {
return v, err
}
case reflect.String:
v.SetString(r.String())
case reflect.Bool:
v.SetBool(true)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v.SetInt(r.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v.SetUint(r.Uint())
case reflect.Float32, reflect.Float64:
v.SetFloat(r.Float())
case reflect.Complex64, reflect.Complex128:
v.SetComplex(r.Complex())
case reflect.Array:
for i := 0; i < v.Len(); i++ {
nv, err := populateValue(v.Index(i), r)
if err != nil {
return v, xerrors.Errorf("array index %d : %w", i, err)
}
v.Index(i).Set(nv)
}
case reflect.Map:
m := reflect.MakeMap(v.Type())
// Set a value in the map
k := reflect.New(v.Type().Key())
kv := reflect.New(v.Type().Elem())
k, err = populateValue(k, r)
if err != nil {
return v, xerrors.Errorf("map key : %w", err)
}
kv, err = populateValue(kv, r)
if err != nil {
return v, xerrors.Errorf("map value : %w", err)
}
m.SetMapIndex(k, kv)
return m, nil
case reflect.Pointer:
return populateValue(v.Elem(), r)
case reflect.Slice:
s := reflect.MakeSlice(v.Type(), 2, 2)
sv, err := populateValue(reflect.New(v.Type().Elem()), r)
if err != nil {
return v, xerrors.Errorf("slice value : %w", err)
}
s.Index(0).Set(sv)
s.Index(1).Set(sv)
// reflect.AppendSlice(s, sv)
return s, nil
case reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func, reflect.Interface:
// Unsupported
return v, xerrors.Errorf("%s is not supported", v.Kind())
default:
return v, xerrors.Errorf("unsupported kind %s", v.Kind())
}
return v, nil
}