1package mox
2
3import (
4 "reflect"
5)
6
7// FillNil returns a modified value with nil maps/slices replaced with empty
8// maps/slices.
9func FillNil(rv reflect.Value) (nv reflect.Value, changed bool) {
10 switch rv.Kind() {
11 case reflect.Struct:
12 for i := 0; i < rv.NumField(); i++ {
13 if !rv.Type().Field(i).IsExported() {
14 continue
15 }
16 vv := rv.Field(i)
17 nvv, ch := FillNil(vv)
18 if ch && !rv.CanSet() {
19 // Make struct settable.
20 nrv := reflect.New(rv.Type()).Elem()
21 for j := 0; j < rv.NumField(); j++ {
22 nrv.Field(j).Set(rv.Field(j))
23 }
24 rv = nrv
25 vv = rv.Field(i)
26 }
27 if ch {
28 changed = true
29 vv.Set(nvv)
30 }
31 }
32 case reflect.Slice:
33 if rv.IsNil() {
34 return reflect.MakeSlice(rv.Type(), 0, 0), true
35 }
36 n := rv.Len()
37 for i := 0; i < n; i++ {
38 rve := rv.Index(i)
39 nrv, ch := FillNil(rve)
40 if ch {
41 changed = true
42 rve.Set(nrv)
43 }
44 }
45 case reflect.Map:
46 if rv.IsNil() {
47 return reflect.MakeMap(rv.Type()), true
48 }
49 i := rv.MapRange()
50 for i.Next() {
51 erv, ch := FillNil(i.Value())
52 if ch {
53 changed = true
54 rv.SetMapIndex(i.Key(), erv)
55 }
56 }
57 case reflect.Pointer:
58 if !rv.IsNil() {
59 FillNil(rv.Elem())
60 }
61 }
62 return rv, changed
63}
64
65// FillExample returns a modified value with nil/empty maps/slices/pointers values
66// replaced with non-empty versions, for more helpful examples of types. Useful for
67// documenting JSON representations of types.
68func FillExample(seen []reflect.Type, rv reflect.Value) reflect.Value {
69 if seen == nil {
70 seen = make([]reflect.Type, 100)
71 }
72
73 // Prevent recursive filling.
74 rvt := rv.Type()
75 index := -1
76 for i, t := range seen {
77 if t == rvt {
78 return rv
79 } else if t == nil {
80 index = i
81 }
82 }
83 if index < 0 {
84 return rv
85 }
86 seen[index] = rvt
87 defer func() {
88 seen[index] = nil
89 }()
90
91 switch rv.Kind() {
92 case reflect.Struct:
93 for i := 0; i < rv.NumField(); i++ {
94 if !rvt.Field(i).IsExported() {
95 continue
96 }
97 vv := rv.Field(i)
98 vv.Set(FillExample(seen, vv))
99 }
100 case reflect.Slice:
101 ev := FillExample(seen, reflect.New(rvt.Elem()).Elem())
102 return reflect.Append(rv, ev)
103 case reflect.Map:
104 vv := FillExample(seen, reflect.New(rvt.Elem()).Elem())
105 nv := reflect.MakeMap(rvt)
106 nv.SetMapIndex(reflect.ValueOf("example"), vv)
107 return nv
108 case reflect.Pointer:
109 nv := reflect.New(rvt.Elem())
110 return FillExample(seen, nv.Elem()).Addr()
111 }
112 return rv
113}
114