-
Notifications
You must be signed in to change notification settings - Fork 0
/
iterate.go
70 lines (66 loc) · 1.77 KB
/
iterate.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
package goutils
import (
"fmt"
"reflect"
)
// IteratorCallback
// Parameters:
//
// path position as string;
// depth element depth in the structure;
// kind - kind of the element;
// element - item tp iterate over;
//
// return false if stop iterating
type IteratorCallback func(path string, depth int, kind reflect.Kind, element interface{}) bool
// IterateDeep - iterate over the structure element. For each element callback function is called.
// All pointers are passed into the callback as value.
func IterateDeep(element interface{}, callback IteratorCallback) {
iterateDeep(".", 0, reflect.ValueOf(element), callback)
}
func iterateDeep(path string, depth int, val reflect.Value, callback IteratorCallback) bool {
kind := val.Kind()
switch kind {
case reflect.Pointer:
uptrValue := val.Elem()
return iterateDeep(path, depth, uptrValue, callback)
case reflect.Slice, reflect.Array:
for i := 0; i < val.Len(); i++ {
elementValue := val.Index(i)
if !iterateDeep(fmt.Sprintf("%s[%d]", path, i), depth+1, elementValue, callback) {
return false
}
}
return true
case reflect.Struct:
if path == "." {
path = ""
}
for i := 0; i < val.NumField(); i++ {
ft := val.Type().Field(i)
if !ft.IsExported() {
continue
}
fieldName := ft.Name
fieldValue := val.FieldByName(fieldName)
if !iterateDeep(fmt.Sprintf("%s.%s", path, fieldName), depth+1, fieldValue, callback) {
return false
}
}
return true
case reflect.Map:
if path == "." {
path = ""
}
for _, mapKey := range val.MapKeys() {
mapValue := val.MapIndex(mapKey)
if !iterateDeep(fmt.Sprintf("%s.%s", path, mapKey), depth+1, mapValue, callback) {
return false
}
}
return true
default:
element := val.Interface()
return callback(path, depth, kind, element)
}
}