You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
6.3 KiB

  1. package toml
  2. // Struct field handling is adapted from code in encoding/json:
  3. //
  4. // Copyright 2010 The Go Authors. All rights reserved.
  5. // Use of this source code is governed by a BSD-style
  6. // license that can be found in the Go distribution.
  7. import (
  8. "reflect"
  9. "sort"
  10. "sync"
  11. )
  12. // A field represents a single field found in a struct.
  13. type field struct {
  14. name string // the name of the field (`toml` tag included)
  15. tag bool // whether field has a `toml` tag
  16. index []int // represents the depth of an anonymous field
  17. typ reflect.Type // the type of the field
  18. }
  19. // byName sorts field by name, breaking ties with depth,
  20. // then breaking ties with "name came from toml tag", then
  21. // breaking ties with index sequence.
  22. type byName []field
  23. func (x byName) Len() int { return len(x) }
  24. func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
  25. func (x byName) Less(i, j int) bool {
  26. if x[i].name != x[j].name {
  27. return x[i].name < x[j].name
  28. }
  29. if len(x[i].index) != len(x[j].index) {
  30. return len(x[i].index) < len(x[j].index)
  31. }
  32. if x[i].tag != x[j].tag {
  33. return x[i].tag
  34. }
  35. return byIndex(x).Less(i, j)
  36. }
  37. // byIndex sorts field by index sequence.
  38. type byIndex []field
  39. func (x byIndex) Len() int { return len(x) }
  40. func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
  41. func (x byIndex) Less(i, j int) bool {
  42. for k, xik := range x[i].index {
  43. if k >= len(x[j].index) {
  44. return false
  45. }
  46. if xik != x[j].index[k] {
  47. return xik < x[j].index[k]
  48. }
  49. }
  50. return len(x[i].index) < len(x[j].index)
  51. }
  52. // typeFields returns a list of fields that TOML should recognize for the given
  53. // type. The algorithm is breadth-first search over the set of structs to
  54. // include - the top struct and then any reachable anonymous structs.
  55. func typeFields(t reflect.Type) []field {
  56. // Anonymous fields to explore at the current level and the next.
  57. current := []field{}
  58. next := []field{{typ: t}}
  59. // Count of queued names for current level and the next.
  60. var count map[reflect.Type]int
  61. var nextCount map[reflect.Type]int
  62. // Types already visited at an earlier level.
  63. visited := map[reflect.Type]bool{}
  64. // Fields found.
  65. var fields []field
  66. for len(next) > 0 {
  67. current, next = next, current[:0]
  68. count, nextCount = nextCount, map[reflect.Type]int{}
  69. for _, f := range current {
  70. if visited[f.typ] {
  71. continue
  72. }
  73. visited[f.typ] = true
  74. // Scan f.typ for fields to include.
  75. for i := 0; i < f.typ.NumField(); i++ {
  76. sf := f.typ.Field(i)
  77. if sf.PkgPath != "" && !sf.Anonymous { // unexported
  78. continue
  79. }
  80. opts := getOptions(sf.Tag)
  81. if opts.skip {
  82. continue
  83. }
  84. index := make([]int, len(f.index)+1)
  85. copy(index, f.index)
  86. index[len(f.index)] = i
  87. ft := sf.Type
  88. if ft.Name() == "" && ft.Kind() == reflect.Ptr {
  89. // Follow pointer.
  90. ft = ft.Elem()
  91. }
  92. // Record found field and index sequence.
  93. if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
  94. tagged := opts.name != ""
  95. name := opts.name
  96. if name == "" {
  97. name = sf.Name
  98. }
  99. fields = append(fields, field{name, tagged, index, ft})
  100. if count[f.typ] > 1 {
  101. // If there were multiple instances, add a second,
  102. // so that the annihilation code will see a duplicate.
  103. // It only cares about the distinction between 1 or 2,
  104. // so don't bother generating any more copies.
  105. fields = append(fields, fields[len(fields)-1])
  106. }
  107. continue
  108. }
  109. // Record new anonymous struct to explore in next round.
  110. nextCount[ft]++
  111. if nextCount[ft] == 1 {
  112. f := field{name: ft.Name(), index: index, typ: ft}
  113. next = append(next, f)
  114. }
  115. }
  116. }
  117. }
  118. sort.Sort(byName(fields))
  119. // Delete all fields that are hidden by the Go rules for embedded fields,
  120. // except that fields with TOML tags are promoted.
  121. // The fields are sorted in primary order of name, secondary order
  122. // of field index length. Loop over names; for each name, delete
  123. // hidden fields by choosing the one dominant field that survives.
  124. out := fields[:0]
  125. for advance, i := 0, 0; i < len(fields); i += advance {
  126. // One iteration per name.
  127. // Find the sequence of fields with the name of this first field.
  128. fi := fields[i]
  129. name := fi.name
  130. for advance = 1; i+advance < len(fields); advance++ {
  131. fj := fields[i+advance]
  132. if fj.name != name {
  133. break
  134. }
  135. }
  136. if advance == 1 { // Only one field with this name
  137. out = append(out, fi)
  138. continue
  139. }
  140. dominant, ok := dominantField(fields[i : i+advance])
  141. if ok {
  142. out = append(out, dominant)
  143. }
  144. }
  145. fields = out
  146. sort.Sort(byIndex(fields))
  147. return fields
  148. }
  149. // dominantField looks through the fields, all of which are known to
  150. // have the same name, to find the single field that dominates the
  151. // others using Go's embedding rules, modified by the presence of
  152. // TOML tags. If there are multiple top-level fields, the boolean
  153. // will be false: This condition is an error in Go and we skip all
  154. // the fields.
  155. func dominantField(fields []field) (field, bool) {
  156. // The fields are sorted in increasing index-length order. The winner
  157. // must therefore be one with the shortest index length. Drop all
  158. // longer entries, which is easy: just truncate the slice.
  159. length := len(fields[0].index)
  160. tagged := -1 // Index of first tagged field.
  161. for i, f := range fields {
  162. if len(f.index) > length {
  163. fields = fields[:i]
  164. break
  165. }
  166. if f.tag {
  167. if tagged >= 0 {
  168. // Multiple tagged fields at the same level: conflict.
  169. // Return no field.
  170. return field{}, false
  171. }
  172. tagged = i
  173. }
  174. }
  175. if tagged >= 0 {
  176. return fields[tagged], true
  177. }
  178. // All remaining fields have the same length. If there's more than one,
  179. // we have a conflict (two fields named "X" at the same level) and we
  180. // return no field.
  181. if len(fields) > 1 {
  182. return field{}, false
  183. }
  184. return fields[0], true
  185. }
  186. var fieldCache struct {
  187. sync.RWMutex
  188. m map[reflect.Type][]field
  189. }
  190. // cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
  191. func cachedTypeFields(t reflect.Type) []field {
  192. fieldCache.RLock()
  193. f := fieldCache.m[t]
  194. fieldCache.RUnlock()
  195. if f != nil {
  196. return f
  197. }
  198. // Compute fields without lock.
  199. // Might duplicate effort but won't hold other computations back.
  200. f = typeFields(t)
  201. if f == nil {
  202. f = []field{}
  203. }
  204. fieldCache.Lock()
  205. if fieldCache.m == nil {
  206. fieldCache.m = map[reflect.Type][]field{}
  207. }
  208. fieldCache.m[t] = f
  209. fieldCache.Unlock()
  210. return f
  211. }