accessors.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package objx
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. )
  8. // arrayAccesRegexString is the regex used to extract the array number
  9. // from the access path
  10. const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
  11. // arrayAccesRegex is the compiled arrayAccesRegexString
  12. var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
  13. // Get gets the value using the specified selector and
  14. // returns it inside a new Obj object.
  15. //
  16. // If it cannot find the value, Get will return a nil
  17. // value inside an instance of Obj.
  18. //
  19. // Get can only operate directly on map[string]interface{} and []interface.
  20. //
  21. // Example
  22. //
  23. // To access the title of the third chapter of the second book, do:
  24. //
  25. // o.Get("books[1].chapters[2].title")
  26. func (m Map) Get(selector string) *Value {
  27. rawObj := access(m, selector, nil, false, false)
  28. return &Value{data: rawObj}
  29. }
  30. // Set sets the value using the specified selector and
  31. // returns the object on which Set was called.
  32. //
  33. // Set can only operate directly on map[string]interface{} and []interface
  34. //
  35. // Example
  36. //
  37. // To set the title of the third chapter of the second book, do:
  38. //
  39. // o.Set("books[1].chapters[2].title","Time to Go")
  40. func (m Map) Set(selector string, value interface{}) Map {
  41. access(m, selector, value, true, false)
  42. return m
  43. }
  44. // access accesses the object using the selector and performs the
  45. // appropriate action.
  46. func access(current, selector, value interface{}, isSet, panics bool) interface{} {
  47. switch selector.(type) {
  48. case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
  49. if array, ok := current.([]interface{}); ok {
  50. index := intFromInterface(selector)
  51. if index >= len(array) {
  52. if panics {
  53. panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
  54. }
  55. return nil
  56. }
  57. return array[index]
  58. }
  59. return nil
  60. case string:
  61. selStr := selector.(string)
  62. selSegs := strings.SplitN(selStr, PathSeparator, 2)
  63. thisSel := selSegs[0]
  64. index := -1
  65. var err error
  66. // https://github.com/stretchr/objx/issues/12
  67. if strings.Contains(thisSel, "[") {
  68. arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel)
  69. if len(arrayMatches) > 0 {
  70. // Get the key into the map
  71. thisSel = arrayMatches[1]
  72. // Get the index into the array at the key
  73. index, err = strconv.Atoi(arrayMatches[2])
  74. if err != nil {
  75. // This should never happen. If it does, something has gone
  76. // seriously wrong. Panic.
  77. panic("objx: Array index is not an integer. Must use array[int].")
  78. }
  79. }
  80. }
  81. if curMap, ok := current.(Map); ok {
  82. current = map[string]interface{}(curMap)
  83. }
  84. // get the object in question
  85. switch current.(type) {
  86. case map[string]interface{}:
  87. curMSI := current.(map[string]interface{})
  88. if len(selSegs) <= 1 && isSet {
  89. curMSI[thisSel] = value
  90. return nil
  91. } else {
  92. current = curMSI[thisSel]
  93. }
  94. default:
  95. current = nil
  96. }
  97. if current == nil && panics {
  98. panic(fmt.Sprintf("objx: '%v' invalid on object.", selector))
  99. }
  100. // do we need to access the item of an array?
  101. if index > -1 {
  102. if array, ok := current.([]interface{}); ok {
  103. if index < len(array) {
  104. current = array[index]
  105. } else {
  106. if panics {
  107. panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
  108. }
  109. current = nil
  110. }
  111. }
  112. }
  113. if len(selSegs) > 1 {
  114. current = access(current, selSegs[1], value, isSet, panics)
  115. }
  116. }
  117. return current
  118. }
  119. // intFromInterface converts an interface object to the largest
  120. // representation of an unsigned integer using a type switch and
  121. // assertions
  122. func intFromInterface(selector interface{}) int {
  123. var value int
  124. switch selector.(type) {
  125. case int:
  126. value = selector.(int)
  127. case int8:
  128. value = int(selector.(int8))
  129. case int16:
  130. value = int(selector.(int16))
  131. case int32:
  132. value = int(selector.(int32))
  133. case int64:
  134. value = int(selector.(int64))
  135. case uint:
  136. value = int(selector.(uint))
  137. case uint8:
  138. value = int(selector.(uint8))
  139. case uint16:
  140. value = int(selector.(uint16))
  141. case uint32:
  142. value = int(selector.(uint32))
  143. case uint64:
  144. value = int(selector.(uint64))
  145. default:
  146. panic("objx: array access argument is not an integer type (this should never happen)")
  147. }
  148. return value
  149. }