result.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. // Copyright (c) 2019 Uber Technologies, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package dig
  21. import (
  22. "errors"
  23. "fmt"
  24. "reflect"
  25. "go.uber.org/dig/internal/dot"
  26. )
  27. // The result interface represents a result produced by a constructor.
  28. //
  29. // The following implementations exist:
  30. // resultList All values returned by the constructor.
  31. // resultSingle A single value produced by a constructor.
  32. // resultObject dig.Out struct where each field in the struct can be
  33. // another result.
  34. // resultGrouped A value produced by a constructor that is part of a value
  35. // group.
  36. type result interface {
  37. // Extracts the values for this result from the provided value and
  38. // stores them into the provided containerWriter.
  39. //
  40. // This MAY panic if the result does not consume a single value.
  41. Extract(containerWriter, reflect.Value)
  42. // DotResult returns a slice of dot.Result(s).
  43. DotResult() []*dot.Result
  44. }
  45. var (
  46. _ result = resultSingle{}
  47. _ result = resultObject{}
  48. _ result = resultList{}
  49. _ result = resultGrouped{}
  50. )
  51. type resultOptions struct {
  52. // If set, this is the name of the associated result value.
  53. //
  54. // For Result Objects, name:".." tags on fields override this.
  55. Name string
  56. Group string
  57. }
  58. // newResult builds a result from the given type.
  59. func newResult(t reflect.Type, opts resultOptions) (result, error) {
  60. switch {
  61. case IsIn(t) || (t.Kind() == reflect.Ptr && IsIn(t.Elem())) || embedsType(t, _inPtrType):
  62. return nil, errf("cannot provide parameter objects", "%v embeds a dig.In", t)
  63. case isError(t):
  64. return nil, errf("cannot return an error here, return it from the constructor instead")
  65. case IsOut(t):
  66. return newResultObject(t, opts)
  67. case embedsType(t, _outPtrType):
  68. return nil, errf(
  69. "cannot build a result object by embedding *dig.Out, embed dig.Out instead",
  70. "%v embeds *dig.Out", t)
  71. case t.Kind() == reflect.Ptr && IsOut(t.Elem()):
  72. return nil, errf(
  73. "cannot return a pointer to a result object, use a value instead",
  74. "%v is a pointer to a struct that embeds dig.Out", t)
  75. case len(opts.Group) > 0:
  76. g, err := parseGroupString(opts.Group)
  77. if err != nil {
  78. return nil, errf(
  79. "cannot parse group %q", opts.Group, err)
  80. }
  81. rg := resultGrouped{Type: t, Group: g.Name, Flatten: g.Flatten}
  82. if g.Flatten {
  83. if t.Kind() != reflect.Slice {
  84. return nil, errf(
  85. "flatten can be applied to slices only",
  86. "%v is not a slice", t)
  87. }
  88. rg.Type = rg.Type.Elem()
  89. }
  90. return rg, nil
  91. default:
  92. return resultSingle{Type: t, Name: opts.Name}, nil
  93. }
  94. }
  95. // resultVisitor visits every result in a result tree, allowing tracking state
  96. // at each level.
  97. type resultVisitor interface {
  98. // Visit is called on the result being visited.
  99. //
  100. // If Visit returns a non-nil resultVisitor, that resultVisitor visits all
  101. // the child results of this result.
  102. Visit(result) resultVisitor
  103. // AnnotateWithField is called on each field of a resultObject after
  104. // visiting it but before walking its descendants.
  105. //
  106. // The same resultVisitor is used for all fields: the one returned upon
  107. // visiting the resultObject.
  108. //
  109. // For each visited field, if AnnotateWithField returns a non-nil
  110. // resultVisitor, it will be used to walk the result of that field.
  111. AnnotateWithField(resultObjectField) resultVisitor
  112. // AnnotateWithPosition is called with the index of each result of a
  113. // resultList after vising it but before walking its descendants.
  114. //
  115. // The same resultVisitor is used for all results: the one returned upon
  116. // visiting the resultList.
  117. //
  118. // For each position, if AnnotateWithPosition returns a non-nil
  119. // resultVisitor, it will be used to walk the result at that index.
  120. AnnotateWithPosition(idx int) resultVisitor
  121. }
  122. // walkResult walks the result tree for the given result with the provided
  123. // visitor.
  124. //
  125. // resultVisitor.Visit will be called on the provided result and if a non-nil
  126. // resultVisitor is received, it will be used to walk its descendants. If a
  127. // resultObject or resultList was visited, AnnotateWithField and
  128. // AnnotateWithPosition respectively will be called before visiting the
  129. // descendants of that resultObject/resultList.
  130. //
  131. // This is very similar to how go/ast.Walk works.
  132. func walkResult(r result, v resultVisitor) {
  133. v = v.Visit(r)
  134. if v == nil {
  135. return
  136. }
  137. switch res := r.(type) {
  138. case resultSingle, resultGrouped:
  139. // No sub-results
  140. case resultObject:
  141. w := v
  142. for _, f := range res.Fields {
  143. if v := w.AnnotateWithField(f); v != nil {
  144. walkResult(f.Result, v)
  145. }
  146. }
  147. case resultList:
  148. w := v
  149. for i, r := range res.Results {
  150. if v := w.AnnotateWithPosition(i); v != nil {
  151. walkResult(r, v)
  152. }
  153. }
  154. default:
  155. panic(fmt.Sprintf(
  156. "It looks like you have found a bug in dig. "+
  157. "Please file an issue at https://github.com/uber-go/dig/issues/ "+
  158. "and provide the following message: "+
  159. "received unknown result type %T", res))
  160. }
  161. }
  162. // resultList holds all values returned by the constructor as results.
  163. type resultList struct {
  164. ctype reflect.Type
  165. Results []result
  166. // For each item at index i returned by the constructor, resultIndexes[i]
  167. // is the index in .Results for the corresponding result object.
  168. // resultIndexes[i] is -1 for errors returned by constructors.
  169. resultIndexes []int
  170. }
  171. func (rl resultList) DotResult() []*dot.Result {
  172. var types []*dot.Result
  173. for _, result := range rl.Results {
  174. types = append(types, result.DotResult()...)
  175. }
  176. return types
  177. }
  178. func newResultList(ctype reflect.Type, opts resultOptions) (resultList, error) {
  179. rl := resultList{
  180. ctype: ctype,
  181. Results: make([]result, 0, ctype.NumOut()),
  182. resultIndexes: make([]int, ctype.NumOut()),
  183. }
  184. resultIdx := 0
  185. for i := 0; i < ctype.NumOut(); i++ {
  186. t := ctype.Out(i)
  187. if isError(t) {
  188. rl.resultIndexes[i] = -1
  189. continue
  190. }
  191. r, err := newResult(t, opts)
  192. if err != nil {
  193. return rl, errf("bad result %d", i+1, err)
  194. }
  195. rl.Results = append(rl.Results, r)
  196. rl.resultIndexes[i] = resultIdx
  197. resultIdx++
  198. }
  199. return rl, nil
  200. }
  201. func (resultList) Extract(containerWriter, reflect.Value) {
  202. panic("It looks like you have found a bug in dig. " +
  203. "Please file an issue at https://github.com/uber-go/dig/issues/ " +
  204. "and provide the following message: " +
  205. "resultList.Extract() must never be called")
  206. }
  207. func (rl resultList) ExtractList(cw containerWriter, values []reflect.Value) error {
  208. for i, v := range values {
  209. if resultIdx := rl.resultIndexes[i]; resultIdx >= 0 {
  210. rl.Results[resultIdx].Extract(cw, v)
  211. continue
  212. }
  213. if err, _ := v.Interface().(error); err != nil {
  214. return err
  215. }
  216. }
  217. return nil
  218. }
  219. // resultSingle is an explicit value produced by a constructor, optionally
  220. // with a name.
  221. //
  222. // This object will be added to the graph as-is.
  223. type resultSingle struct {
  224. Name string
  225. Type reflect.Type
  226. }
  227. func (rs resultSingle) DotResult() []*dot.Result {
  228. return []*dot.Result{
  229. {
  230. Node: &dot.Node{
  231. Type: rs.Type,
  232. Name: rs.Name,
  233. },
  234. },
  235. }
  236. }
  237. func (rs resultSingle) Extract(cw containerWriter, v reflect.Value) {
  238. cw.setValue(rs.Name, rs.Type, v)
  239. }
  240. // resultObject is a dig.Out struct where each field is another result.
  241. //
  242. // This object is not added to the graph. Its fields are interpreted as
  243. // results and added to the graph if needed.
  244. type resultObject struct {
  245. Type reflect.Type
  246. Fields []resultObjectField
  247. }
  248. func (ro resultObject) DotResult() []*dot.Result {
  249. var types []*dot.Result
  250. for _, field := range ro.Fields {
  251. types = append(types, field.DotResult()...)
  252. }
  253. return types
  254. }
  255. func newResultObject(t reflect.Type, opts resultOptions) (resultObject, error) {
  256. ro := resultObject{Type: t}
  257. if len(opts.Name) > 0 {
  258. return ro, errf(
  259. "cannot specify a name for result objects", "%v embeds dig.Out", t)
  260. }
  261. if len(opts.Group) > 0 {
  262. return ro, errf(
  263. "cannot specify a group for result objects", "%v embeds dig.Out", t)
  264. }
  265. for i := 0; i < t.NumField(); i++ {
  266. f := t.Field(i)
  267. if f.Type == _outType {
  268. // Skip over the dig.Out embed.
  269. continue
  270. }
  271. rof, err := newResultObjectField(i, f, opts)
  272. if err != nil {
  273. return ro, errf("bad field %q of %v", f.Name, t, err)
  274. }
  275. ro.Fields = append(ro.Fields, rof)
  276. }
  277. return ro, nil
  278. }
  279. func (ro resultObject) Extract(cw containerWriter, v reflect.Value) {
  280. for _, f := range ro.Fields {
  281. f.Result.Extract(cw, v.Field(f.FieldIndex))
  282. }
  283. }
  284. // resultObjectField is a single field inside a dig.Out struct.
  285. type resultObjectField struct {
  286. // Name of the field in the struct.
  287. FieldName string
  288. // Index of the field in the struct.
  289. //
  290. // We need to track this separately because not all fields of the struct
  291. // map to results.
  292. FieldIndex int
  293. // Result produced by this field.
  294. Result result
  295. }
  296. func (rof resultObjectField) DotResult() []*dot.Result {
  297. return rof.Result.DotResult()
  298. }
  299. // newResultObjectField(i, f, opts) builds a resultObjectField from the field
  300. // f at index i.
  301. func newResultObjectField(idx int, f reflect.StructField, opts resultOptions) (resultObjectField, error) {
  302. rof := resultObjectField{
  303. FieldName: f.Name,
  304. FieldIndex: idx,
  305. }
  306. var r result
  307. switch {
  308. case f.PkgPath != "":
  309. return rof, errf(
  310. "unexported fields not allowed in dig.Out, did you mean to export %q (%v)?", f.Name, f.Type)
  311. case f.Tag.Get(_groupTag) != "":
  312. var err error
  313. r, err = newResultGrouped(f)
  314. if err != nil {
  315. return rof, err
  316. }
  317. default:
  318. var err error
  319. if name := f.Tag.Get(_nameTag); len(name) > 0 {
  320. // can modify in-place because options are passed-by-value.
  321. opts.Name = name
  322. }
  323. r, err = newResult(f.Type, opts)
  324. if err != nil {
  325. return rof, err
  326. }
  327. }
  328. rof.Result = r
  329. return rof, nil
  330. }
  331. // resultGrouped is a value produced by a constructor that is part of a result
  332. // group.
  333. //
  334. // These will be produced as fields of a dig.Out struct.
  335. type resultGrouped struct {
  336. // Name of the group as specified in the `group:".."` tag.
  337. Group string
  338. // Type of value produced.
  339. Type reflect.Type
  340. // Indicates elements of a value are to be injected individually, instead of
  341. // as a group. Requires the value's slice to be a group. If set, Type will be
  342. // the type of individual elements rather than the group.
  343. Flatten bool
  344. }
  345. func (rt resultGrouped) DotResult() []*dot.Result {
  346. return []*dot.Result{
  347. {
  348. Node: &dot.Node{
  349. Type: rt.Type,
  350. Group: rt.Group,
  351. },
  352. },
  353. }
  354. }
  355. // newResultGrouped(f) builds a new resultGrouped from the provided field.
  356. func newResultGrouped(f reflect.StructField) (resultGrouped, error) {
  357. g, err := parseGroupString(f.Tag.Get(_groupTag))
  358. if err != nil {
  359. return resultGrouped{}, err
  360. }
  361. rg := resultGrouped{
  362. Group: g.Name,
  363. Flatten: g.Flatten,
  364. Type: f.Type,
  365. }
  366. name := f.Tag.Get(_nameTag)
  367. optional, _ := isFieldOptional(f)
  368. switch {
  369. case g.Flatten && f.Type.Kind() != reflect.Slice:
  370. return rg, errf("flatten can be applied to slices only",
  371. "field %q (%v) is not a slice", f.Name, f.Type)
  372. case name != "":
  373. return rg, errf(
  374. "cannot use named values with value groups",
  375. "name:%q provided with group:%q", name, rg.Group)
  376. case optional:
  377. return rg, errors.New("value groups cannot be optional")
  378. }
  379. if g.Flatten {
  380. rg.Type = f.Type.Elem()
  381. }
  382. return rg, nil
  383. }
  384. func (rt resultGrouped) Extract(cw containerWriter, v reflect.Value) {
  385. if !rt.Flatten {
  386. cw.submitGroupedValue(rt.Group, rt.Type, v)
  387. return
  388. }
  389. for i := 0; i < v.Len(); i++ {
  390. cw.submitGroupedValue(rt.Group, rt.Type, v.Index(i))
  391. }
  392. }