github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/prepared_query/walk.go (about) 1 package prepared_query 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 // visitor is a function that will get called for each string element of a 9 // structure. 10 type visitor func(path string, v reflect.Value) error 11 12 // visit calls the visitor function for each string it finds, and will descend 13 // recursively into structures and slices. If any visitor returns an error then 14 // the search will stop and that error will be returned. 15 func visit(path string, v reflect.Value, t reflect.Type, fn visitor) error { 16 switch v.Kind() { 17 case reflect.String: 18 return fn(path, v) 19 case reflect.Struct: 20 for i := 0; i < v.NumField(); i++ { 21 vf := v.Field(i) 22 tf := t.Field(i) 23 newPath := fmt.Sprintf("%s.%s", path, tf.Name) 24 if err := visit(newPath, vf, tf.Type, fn); err != nil { 25 return err 26 } 27 } 28 case reflect.Slice: 29 for i := 0; i < v.Len(); i++ { 30 vi := v.Index(i) 31 ti := vi.Type() 32 newPath := fmt.Sprintf("%s[%d]", path, i) 33 if err := visit(newPath, vi, ti, fn); err != nil { 34 return err 35 } 36 } 37 case reflect.Map: 38 for _, key := range v.MapKeys() { 39 value := v.MapIndex(key) 40 41 newValue := reflect.New(value.Type()).Elem() 42 newValue.SetString(value.String()) 43 44 if err := visit(fmt.Sprintf("%s[%s]", path, key.String()), newValue, newValue.Type(), fn); err != nil { 45 return err 46 } 47 48 // overwrite the entry in case it was modified by the callback 49 v.SetMapIndex(key, newValue) 50 } 51 } 52 return nil 53 } 54 55 // walk finds all the string elements of a given structure (and its sub- 56 // structures) and calls the visitor function. Each string found will get 57 // a unique path computed. If any visitor returns an error then the search 58 // will stop and that error will be returned. 59 func walk(obj interface{}, fn visitor) error { 60 v := reflect.ValueOf(obj).Elem() 61 t := v.Type() 62 return visit("", v, t, fn) 63 }