uber-go / dig
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

21
package dig
22

23
import (
24
	"container/list"
25
	"reflect"
26
)
27

28
var (
29
	_noValue    reflect.Value
30
	_errType    = reflect.TypeOf((*error)(nil)).Elem()
31
	_inPtrType  = reflect.TypeOf((*In)(nil))
32
	_inType     = reflect.TypeOf(In{})
33
	_outPtrType = reflect.TypeOf((*Out)(nil))
34
	_outType    = reflect.TypeOf(Out{})
35
)
36

37
// Special interface embedded inside dig sentinel values (dig.In, dig.Out) to
38
// make their special nature obvious in the godocs. Otherwise they will appear
39
// as plain empty structs.
40
type digSentinel interface {
41
	digSentinel()
42
}
43

44
// In may be embedded into structs to request dig to treat them as special
45
// parameter structs. When a constructor accepts such a struct, instead of the
46
// struct becoming a dependency for that constructor, all its fields become
47
// dependencies instead. See the section on Parameter Objects in the
48
// package-level documentation for more information.
49
//
50
// Fields of the struct may optionally be tagged to customize the behavior of
51
// dig. The following tags are supported,
52
//
53
//   name        Requests a value with the same name and type from the
54
//               container. See Named Values for more information.
55
//   optional    If set to true, indicates that the dependency is optional and
56
//               the constructor gracefully handles its absence.
57
//   group       Name of the Value Group from which this field will be filled.
58
//               The field must be a slice type. See Value Groups in the
59
//               package documentation for more information.
60
type In struct{ digSentinel }
61

62
// Out is an embeddable type that signals to dig that the returned
63
// struct should be treated differently. Instead of the struct itself
64
// becoming part of the container, all members of the struct will.
65

66
// Out may be embedded into structs to request dig to treat them as special
67
// result structs. When a constructor returns such a struct, instead of the
68
// struct becoming a result of the constructor, all its fields become results
69
// of the constructor. See the section on Result Objects in the package-level
70
// documentation for more information.
71
//
72
// Fields of the struct may optionally be tagged to customize the behavior of
73
// dig. The following tags are supported,
74
//
75
//   name        Specifies the name of the value. Only a field on a dig.In
76
//               struct with the same 'name' annotation can receive this
77
//               value. See Named Values for more information.
78
//   group       Name of the Value Group to which this field's value is being
79
//               sent. See Value Groups in the package documentation for more
80
//               information.
81
type Out struct{ digSentinel }
82

83
func isError(t reflect.Type) bool {
84 2
	return t.Implements(_errType)
85
}
86

87
// IsIn checks whether the given struct is a dig.In struct. A struct qualifies
88
// as a dig.In struct if it embeds the dig.In type or if any struct that it
89
// embeds is a dig.In struct. The parameter may be the reflect.Type of the
90
// struct rather than the struct itself.
91
//
92
// A struct MUST qualify as a dig.In struct for its fields to be treated
93
// specially by dig.
94
//
95
// See the documentation for dig.In for a comprehensive list of supported
96
// tags.
97
func IsIn(o interface{}) bool {
98 2
	return embedsType(o, _inType)
99
}
100

101
// IsOut checks whether the given struct is a dig.Out struct. A struct
102
// qualifies as a dig.Out struct if it embeds the dig.Out type or if any
103
// struct that it embeds is a dig.Out struct. The parameter may be the
104
// reflect.Type of the struct rather than the struct itself.
105
//
106
// A struct MUST qualify as a dig.Out struct for its fields to be treated
107
// specially by dig.
108
//
109
// See the documentation for dig.Out for a comprehensive list of supported
110
// tags.
111
func IsOut(o interface{}) bool {
112 2
	return embedsType(o, _outType)
113
}
114

115
// Returns true if t embeds e or if any of the types embedded by t embed e.
116
func embedsType(i interface{}, e reflect.Type) bool {
117
	// TODO: this function doesn't consider e being a pointer.
118
	// given `type A foo { *In }`, this function would return false for
119
	// embedding dig.In, which makes for some extra error checking in places
120
	// that call this funciton. Might be worthwhile to consider reflect.Indirect
121
	// usage to clean up the callers.
122

123 2
	if i == nil {
124 2
		return false
125
	}
126

127
	// maybe it's already a reflect.Type
128 2
	t, ok := i.(reflect.Type)
129 2
	if !ok {
130
		// take the type if it's not
131 2
		t = reflect.TypeOf(i)
132
	}
133

134
	// We are going to do a breadth-first search of all embedded fields.
135 2
	types := list.New()
136 2
	types.PushBack(t)
137
	for types.Len() > 0 {
138 2
		t := types.Remove(types.Front()).(reflect.Type)
139

140 2
		if t == e {
141 2
			return true
142
		}
143

144 2
		if t.Kind() != reflect.Struct {
145 2
			continue
146
		}
147

148
		for i := 0; i < t.NumField(); i++ {
149 2
			f := t.Field(i)
150 2
			if f.Anonymous {
151 2
				types.PushBack(f.Type)
152
			}
153
		}
154
	}
155

156
	// If perf is an issue, we can cache known In objects and Out objects in a
157
	// map[reflect.Type]struct{}.
158 2
	return false
159
}

Read our documentation on viewing source code .

Loading