github.com/searKing/golang/go@v1.2.117/reflect/walk.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package reflect 6 7 import ( 8 "reflect" 9 ) 10 11 // Walk walks down v 12 func Walk(t reflect.Type, visitedOnce bool, do func(s reflect.Type, sf reflect.StructField) (stop bool)) { 13 // Anonymous fields to explore at the current level and the next. 14 var current []reflect.Type 15 next := []reflect.Type{t} 16 17 // Count of queued names for current level and the next. 18 currentCount := map[reflect.Type]int{} 19 nextCount := map[reflect.Type]int{} 20 21 // Types already visited at an earlier level. 22 // FIXME I havenot seen any case which can trigger visited 23 visited := map[reflect.Type]bool{} 24 for len(next) > 0 { 25 current, next = next, current[:0] 26 currentCount, nextCount = nextCount, map[reflect.Type]int{} 27 28 for _, typ := range current { 29 30 if typ.Kind() == reflect.Ptr { 31 // Follow pointer. 32 typ = typ.Elem() 33 } 34 if visitedOnce { 35 if visited[typ] { 36 continue 37 } 38 visited[typ] = true 39 } 40 41 if typ.Kind() != reflect.Struct { 42 if do(typ, reflect.StructField{}) { 43 return 44 } 45 continue 46 } 47 // Scan typ for fields to include. 48 for i := 0; i < typ.NumField(); i++ { 49 sf := typ.Field(i) 50 if do(typ, sf) { 51 continue 52 } 53 54 ft := sf.Type 55 if ft.Name() == "" && ft.Kind() == reflect.Ptr { 56 // Follow pointer. 57 ft = ft.Elem() 58 } 59 60 // Record found field and index sequence. 61 if ft.Name() != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { 62 if currentCount[typ] > 1 { 63 } 64 //continue 65 } 66 // Record new anonymous struct to explore in next round. 67 nextCount[ft]++ 68 if !visitedOnce || nextCount[ft] == 1 { 69 next = append(next, ft) 70 } 71 } 72 } 73 } 74 75 }