...

Source file src/github.com/aws/aws-sdk-go-v2/internal/awsutil/path_value.go

Documentation: github.com/aws/aws-sdk-go-v2/internal/awsutil

     1  package awsutil
     2  
     3  import (
     4  	"reflect"
     5  	"regexp"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/jmespath/go-jmespath"
    10  )
    11  
    12  var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)
    13  
    14  // rValuesAtPath returns a slice of values found in value v. The values
    15  // in v are explored recursively so all nested values are collected.
    16  func rValuesAtPath(v interface{}, path string, createPath, caseSensitive, nilTerm bool) []reflect.Value {
    17  	pathparts := strings.Split(path, "||")
    18  	if len(pathparts) > 1 {
    19  		for _, pathpart := range pathparts {
    20  			vals := rValuesAtPath(v, pathpart, createPath, caseSensitive, nilTerm)
    21  			if len(vals) > 0 {
    22  				return vals
    23  			}
    24  		}
    25  		return nil
    26  	}
    27  
    28  	values := []reflect.Value{reflect.Indirect(reflect.ValueOf(v))}
    29  	components := strings.Split(path, ".")
    30  	for len(values) > 0 && len(components) > 0 {
    31  		var index *int64
    32  		var indexStar bool
    33  		c := strings.TrimSpace(components[0])
    34  		if c == "" { // no actual component, illegal syntax
    35  			return nil
    36  		} else if caseSensitive && c != "*" && strings.ToLower(c[0:1]) == c[0:1] {
    37  			// TODO normalize case for user
    38  			return nil // don't support unexported fields
    39  		}
    40  
    41  		// parse this component
    42  		if m := indexRe.FindStringSubmatch(c); m != nil {
    43  			c = m[1]
    44  			if m[2] == "" {
    45  				index = nil
    46  				indexStar = true
    47  			} else {
    48  				i, _ := strconv.ParseInt(m[2], 10, 32)
    49  				index = &i
    50  				indexStar = false
    51  			}
    52  		}
    53  
    54  		nextvals := []reflect.Value{}
    55  		for _, value := range values {
    56  			// pull component name out of struct member
    57  			if value.Kind() != reflect.Struct {
    58  				continue
    59  			}
    60  
    61  			if c == "*" { // pull all members
    62  				for i := 0; i < value.NumField(); i++ {
    63  					if f := reflect.Indirect(value.Field(i)); f.IsValid() {
    64  						nextvals = append(nextvals, f)
    65  					}
    66  				}
    67  				continue
    68  			}
    69  
    70  			value = value.FieldByNameFunc(func(name string) bool {
    71  				if c == name {
    72  					return true
    73  				} else if !caseSensitive && strings.EqualFold(name, c) {
    74  					return true
    75  				}
    76  				return false
    77  			})
    78  
    79  			if nilTerm && value.Kind() == reflect.Ptr && len(components[1:]) == 0 {
    80  				if !value.IsNil() {
    81  					value.Set(reflect.Zero(value.Type()))
    82  				}
    83  				return []reflect.Value{value}
    84  			}
    85  
    86  			if createPath && value.Kind() == reflect.Ptr && value.IsNil() {
    87  				// TODO if the value is the terminus it should not be created
    88  				// if the value to be set to its position is nil.
    89  				value.Set(reflect.New(value.Type().Elem()))
    90  				value = value.Elem()
    91  			} else {
    92  				value = reflect.Indirect(value)
    93  			}
    94  
    95  			if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
    96  				if !createPath && value.IsNil() {
    97  					value = reflect.ValueOf(nil)
    98  				}
    99  			}
   100  
   101  			if value.IsValid() {
   102  				nextvals = append(nextvals, value)
   103  			}
   104  		}
   105  		values = nextvals
   106  
   107  		if indexStar || index != nil {
   108  			nextvals = []reflect.Value{}
   109  			for _, valItem := range values {
   110  				value := reflect.Indirect(valItem)
   111  				if value.Kind() != reflect.Slice {
   112  					continue
   113  				}
   114  
   115  				if indexStar { // grab all indices
   116  					for i := 0; i < value.Len(); i++ {
   117  						idx := reflect.Indirect(value.Index(i))
   118  						if idx.IsValid() {
   119  							nextvals = append(nextvals, idx)
   120  						}
   121  					}
   122  					continue
   123  				}
   124  
   125  				// pull out index
   126  				i := int(*index)
   127  				if i >= value.Len() { // check out of bounds
   128  					if createPath {
   129  						// TODO resize slice
   130  					} else {
   131  						continue
   132  					}
   133  				} else if i < 0 { // support negative indexing
   134  					i = value.Len() + i
   135  				}
   136  				value = reflect.Indirect(value.Index(i))
   137  
   138  				if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
   139  					if !createPath && value.IsNil() {
   140  						value = reflect.ValueOf(nil)
   141  					}
   142  				}
   143  
   144  				if value.IsValid() {
   145  					nextvals = append(nextvals, value)
   146  				}
   147  			}
   148  			values = nextvals
   149  		}
   150  
   151  		components = components[1:]
   152  	}
   153  	return values
   154  }
   155  
   156  // ValuesAtPath returns a list of values at the case insensitive lexical
   157  // path inside of a structure.
   158  func ValuesAtPath(i interface{}, path string) ([]interface{}, error) {
   159  	result, err := jmespath.Search(path, i)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	v := reflect.ValueOf(result)
   165  	if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) {
   166  		return nil, nil
   167  	}
   168  	if s, ok := result.([]interface{}); ok {
   169  		return s, err
   170  	}
   171  	if v.Kind() == reflect.Map && v.Len() == 0 {
   172  		return nil, nil
   173  	}
   174  	if v.Kind() == reflect.Slice {
   175  		out := make([]interface{}, v.Len())
   176  		for i := 0; i < v.Len(); i++ {
   177  			out[i] = v.Index(i).Interface()
   178  		}
   179  		return out, nil
   180  	}
   181  
   182  	return []interface{}{result}, nil
   183  }
   184  
   185  // SetValueAtPath sets a value at the case insensitive lexical path inside
   186  // of a structure.
   187  func SetValueAtPath(i interface{}, path string, v interface{}) {
   188  	if rvals := rValuesAtPath(i, path, true, false, v == nil); rvals != nil {
   189  		for _, rval := range rvals {
   190  			if rval.Kind() == reflect.Ptr && rval.IsNil() {
   191  				continue
   192  			}
   193  			setValue(rval, v)
   194  		}
   195  	}
   196  }
   197  
   198  func setValue(dstVal reflect.Value, src interface{}) {
   199  	if dstVal.Kind() == reflect.Ptr {
   200  		dstVal = reflect.Indirect(dstVal)
   201  	}
   202  	srcVal := reflect.ValueOf(src)
   203  
   204  	if !srcVal.IsValid() { // src is literal nil
   205  		if dstVal.CanAddr() {
   206  			// Convert to pointer so that pointer's value can be nil'ed
   207  			//                     dstVal = dstVal.Addr()
   208  		}
   209  		dstVal.Set(reflect.Zero(dstVal.Type()))
   210  
   211  	} else if srcVal.Kind() == reflect.Ptr {
   212  		if srcVal.IsNil() {
   213  			srcVal = reflect.Zero(dstVal.Type())
   214  		} else {
   215  			srcVal = reflect.ValueOf(src).Elem()
   216  		}
   217  		dstVal.Set(srcVal)
   218  	} else {
   219  		if dstVal.Kind() == reflect.String {
   220  			dstVal.SetString(srcVal.String())
   221  		} else {
   222  			dstVal.Set(srcVal)
   223  		}
   224  	}
   225  }
   226  

View as plain text