-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathzero.go
69 lines (62 loc) · 1.76 KB
/
zero.go
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
package valigo
import (
"fmt"
"reflect"
)
// Zero allocates an input structure such that all pointer fields
// are fully allocated, i.e. rather than having a nil value,
// the pointer contains a pointer to an initialized value,
// e.g. an *int field will be a pointer to 0 instead of a nil pointer.
//
// zero does not allocate private fields.
func zero(obj interface{}) error {
indirectVal := reflect.Indirect(reflect.ValueOf(obj))
if !indirectVal.CanSet() {
return fmt.Errorf("Input interface is not addressable (can't Set the memory address): %#v",
obj)
}
if indirectVal.Kind() != reflect.Struct {
return fmt.Errorf("allocate.Zero currently only works with [pointers to] structs, not type %v",
indirectVal.Kind())
}
// allocate each of the structs fields
var err error
for i := 0; i < indirectVal.NumField(); i++ {
field := indirectVal.Field(i)
// pre-allocate pointer fields
if field.Kind() == reflect.Ptr && field.IsNil() {
if field.CanSet() {
field.Set(reflect.New(field.Type().Elem()))
}
}
indirectField := reflect.Indirect(field)
switch indirectField.Kind() {
case reflect.Map:
indirectField.Set(reflect.MakeMap(indirectField.Type()))
case reflect.Struct:
// recursively allocate each of the structs embedded fields
if field.Kind() == reflect.Ptr {
err = zero(field.Interface())
} else {
// field of Struct can always use field.Addr()
fieldAddr := field.Addr()
if fieldAddr.CanInterface() {
err = zero(fieldAddr.Interface())
} else {
err = fmt.Errorf("struct field can't interface, %#v", fieldAddr)
}
}
}
if err != nil {
return err
}
}
return err
}
// MustZero will panic instead of return error
func mustZero(obj interface{}) {
err := zero(obj)
if err != nil {
panic(err)
}
}