-
Notifications
You must be signed in to change notification settings - Fork 1
/
walker.go
80 lines (66 loc) · 1.73 KB
/
walker.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
70
71
72
73
74
75
76
77
78
79
80
package magicjson
import (
"encoding/json"
"fmt"
"reflect"
)
type callbackFn func(v reflect.Value, marshaller bool, path string) error
func walk(anything any, cb callbackFn) error {
return walker(reflect.TypeOf(anything), reflect.ValueOf(anything), "", cb)
}
func walker(t reflect.Type, v reflect.Value, path string, cb callbackFn) error {
// check if the value is the type of marshaler
if isMarshaler(v) {
return cb(toRef(v), true, path)
}
// de-reference the value when it's a pointer - a value can be a type of marshaler
if t.Kind() == reflect.Ptr && isMarshaler(v.Elem()) {
return cb(v, true, path)
}
if isBytes(v) {
return cb(v, false, path)
}
switch t.Kind() {
case reflect.Struct:
for idx := 0; idx < t.NumField(); idx++ {
field := t.Field(idx)
if err := walker(field.Type, v.Field(idx), addPath(path, field.Name), cb); err != nil {
return err
}
}
case reflect.Slice, reflect.Array:
for idx := 0; idx < v.Len(); idx++ {
item := v.Index(idx)
if err := walker(item.Type(), item, addPath(path, fmt.Sprint(idx)), cb); err != nil {
return err
}
}
case reflect.Map:
for _, key := range v.MapKeys() {
item := v.MapIndex(key)
if err := walker(item.Type(), item, addPath(path, key.String()), cb); err != nil {
return err
}
}
case reflect.Ptr:
return walker(t.Elem(), v.Elem(), path, cb)
default:
return cb(v, false, path)
}
return nil
}
func toRef(v reflect.Value) reflect.Value {
ref := reflect.New(v.Type())
ref.Elem().Set(v)
return ref
}
func isMarshaler(v reflect.Value) bool {
_, ok := reflect.New(v.Type()).Interface().(json.Marshaler)
return ok
}
func addPath(path, suffix string) string {
if len(path) > 0 {
return fmt.Sprint(path, ".", suffix)
}
return suffix
}