path_value.go 5.3 KB

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