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  }