-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathreflect.go
More file actions
144 lines (126 loc) · 3.4 KB
/
Copy pathreflect.go
File metadata and controls
144 lines (126 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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
}