forked from uber-go/dig
-
Notifications
You must be signed in to change notification settings - Fork 0
/
inout.go
175 lines (154 loc) · 6.18 KB
/
inout.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// Copyright (c) 2019 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package dig
import (
"container/list"
"reflect"
"strconv"
)
var (
_noValue reflect.Value
_errType = reflect.TypeOf((*error)(nil)).Elem()
_inPtrType = reflect.TypeOf((*In)(nil))
_inType = reflect.TypeOf(In{})
_outPtrType = reflect.TypeOf((*Out)(nil))
_outType = reflect.TypeOf(Out{})
)
// Placeholder type placed in dig.In/dig.out to make their special nature
// obvious in godocs.
// Otherwise they will appear as plain empty structs.
type digSentinel struct{}
// In may be embedded into structs to request dig to treat them as special
// parameter structs. When a constructor accepts such a struct, instead of the
// struct becoming a dependency for that constructor, all its fields become
// dependencies instead. See the section on Parameter Objects in the
// package-level documentation for more information.
//
// Fields of the struct may optionally be tagged to customize the behavior of
// dig. The following tags are supported,
//
// name Requests a value with the same name and type from the
// container. See Named Values for more information.
// optional If set to true, indicates that the dependency is optional and
// the constructor gracefully handles its absence.
// group Name of the Value Group from which this field will be filled.
// The field must be a slice type. See Value Groups in the
// package documentation for more information.
type In struct{ _ digSentinel }
// Out is an embeddable type that signals to dig that the returned
// struct should be treated differently. Instead of the struct itself
// becoming part of the container, all members of the struct will.
// Out may be embedded into structs to request dig to treat them as special
// result structs. When a constructor returns such a struct, instead of the
// struct becoming a result of the constructor, all its fields become results
// of the constructor. See the section on Result Objects in the package-level
// documentation for more information.
//
// Fields of the struct may optionally be tagged to customize the behavior of
// dig. The following tags are supported,
//
// name Specifies the name of the value. Only a field on a dig.In
// struct with the same 'name' annotation can receive this
// value. See Named Values for more information.
// group Name of the Value Group to which this field's value is being
// sent. See Value Groups in the package documentation for more
// information.
type Out struct{ _ digSentinel }
func isError(t reflect.Type) bool {
return t.Implements(_errType)
}
// IsIn checks whether the given struct is a dig.In struct. A struct qualifies
// as a dig.In struct if it embeds the dig.In type or if any struct that it
// embeds is a dig.In struct. The parameter may be the reflect.Type of the
// struct rather than the struct itself.
//
// A struct MUST qualify as a dig.In struct for its fields to be treated
// specially by dig.
//
// See the documentation for dig.In for a comprehensive list of supported
// tags.
func IsIn(o interface{}) bool {
return embedsType(o, _inType)
}
// IsOut checks whether the given struct is a dig.Out struct. A struct
// qualifies as a dig.Out struct if it embeds the dig.Out type or if any
// struct that it embeds is a dig.Out struct. The parameter may be the
// reflect.Type of the struct rather than the struct itself.
//
// A struct MUST qualify as a dig.Out struct for its fields to be treated
// specially by dig.
//
// See the documentation for dig.Out for a comprehensive list of supported
// tags.
func IsOut(o interface{}) bool {
return embedsType(o, _outType)
}
// Returns true if t embeds e or if any of the types embedded by t embed e.
func embedsType(i interface{}, e reflect.Type) bool {
// TODO: this function doesn't consider e being a pointer.
// given `type A foo { *In }`, this function would return false for
// embedding dig.In, which makes for some extra error checking in places
// that call this function. Might be worthwhile to consider reflect.Indirect
// usage to clean up the callers.
if i == nil {
return false
}
// maybe it's already a reflect.Type
t, ok := i.(reflect.Type)
if !ok {
// take the type if it's not
t = reflect.TypeOf(i)
}
// We are going to do a breadth-first search of all embedded fields.
types := list.New()
types.PushBack(t)
for types.Len() > 0 {
t := types.Remove(types.Front()).(reflect.Type)
if t == e {
return true
}
if t.Kind() != reflect.Struct {
continue
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Anonymous {
types.PushBack(f.Type)
}
}
}
// If perf is an issue, we can cache known In objects and Out objects in a
// map[reflect.Type]struct{}.
return false
}
// Checks if a field of an In struct is optional.
func isFieldOptional(f reflect.StructField) (bool, error) {
tag := f.Tag.Get(_optionalTag)
if tag == "" {
return false, nil
}
optional, err := strconv.ParseBool(tag)
if err != nil {
err = errf(
"invalid value %q for %q tag on field %v",
tag, _optionalTag, f.Name, err)
}
return optional, err
}