1package awsutil 2 3import ( 4 "reflect" 5 "regexp" 6 "strconv" 7 "strings" 8 9 "github.com/jmespath/go-jmespath" 10) 11 12var 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. 16func 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. 158func 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. 187func SetValueAtPath(i interface{}, path string, v interface{}) { 188 rvals := rValuesAtPath(i, path, true, false, v == nil) 189 for _, rval := range rvals { 190 if rval.Kind() == reflect.Ptr && rval.IsNil() { 191 continue 192 } 193 setValue(rval, v) 194 } 195} 196 197func setValue(dstVal reflect.Value, src interface{}) { 198 if dstVal.Kind() == reflect.Ptr { 199 dstVal = reflect.Indirect(dstVal) 200 } 201 srcVal := reflect.ValueOf(src) 202 203 if !srcVal.IsValid() { // src is literal nil 204 if dstVal.CanAddr() { 205 // Convert to pointer so that pointer's value can be nil'ed 206 // dstVal = dstVal.Addr() 207 } 208 dstVal.Set(reflect.Zero(dstVal.Type())) 209 210 } else if srcVal.Kind() == reflect.Ptr { 211 if srcVal.IsNil() { 212 srcVal = reflect.Zero(dstVal.Type()) 213 } else { 214 srcVal = reflect.ValueOf(src).Elem() 215 } 216 dstVal.Set(srcVal) 217 } else { 218 dstVal.Set(srcVal) 219 } 220 221} 222