Skip to content

Commit f5eea4a

Browse files
committed
make Unused{} a special type
1 parent 37b5cae commit f5eea4a

10 files changed

Lines changed: 359 additions & 17 deletions

File tree

api.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -579,11 +579,3 @@ func (c *Collection) MustSetCallback(binderFunction interface{}) {
579579
panic(DetailedError(err))
580580
}
581581
}
582-
583-
type unused struct{}
584-
585-
// Unused is value that a function that returns injectors can return that will never be
586-
// included in an injection chain. Unused can be used in situations where you want to
587-
// call a function as part of generating a list of injectors and functions in such a list
588-
// must have a return value.
589-
var Unused = Provide("unused", unused{})

bind.go

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func doBind(sc *Collection, originalInvokeF *provider, originalInitF *provider,
1414
var invokeF *provider
1515
var initF *provider
1616
var debuggingProvider **provider
17-
funcs := make([]*provider, 0, len(sc.contents)+3)
17+
funcs := make([]*provider, 0, len(sc.contents)+5)
1818
{
1919
var err error
2020
invokeF, err = characterizeInitInvoke(originalInvokeF, charContext{inputsAreStatic: false})
@@ -61,10 +61,46 @@ func doBind(sc *Collection, originalInvokeF *provider, originalInitF *provider,
6161
funcs = append(funcs, invokeF)
6262
funcs = append(funcs, afterInvoke...)
6363

64+
var consumesUnused bool
65+
var receivesUnused bool
6466
for _, fm := range funcs {
6567
if fm.required {
6668
fm.include = true
6769
}
70+
for _, in := range fm.flows[inputParams] {
71+
if in == unusedTypeCode {
72+
consumesUnused = true
73+
}
74+
}
75+
for _, in := range fm.flows[bypassParams] {
76+
if in == unusedTypeCode {
77+
consumesUnused = true
78+
}
79+
}
80+
for _, in := range fm.flows[receivedParams] {
81+
if in == unusedTypeCode {
82+
receivesUnused = true
83+
}
84+
}
85+
}
86+
if consumesUnused {
87+
funcs = append(funcs, nil)
88+
for i := len(funcs) - 1; i > 0; i-- {
89+
funcs[i] = funcs[i-1]
90+
}
91+
d, err := makeUnusedInputProvider()
92+
if err != nil {
93+
return err
94+
}
95+
funcs[0] = d
96+
}
97+
if receivesUnused {
98+
d, err := makeUnusedReturnsProvider()
99+
if err != nil {
100+
return err
101+
}
102+
funcs = append(funcs, d)
103+
funcs[len(funcs)-1], funcs[len(funcs)-2] = funcs[len(funcs)-2], funcs[len(funcs)-1]
68104
}
69105
}
70106

@@ -406,6 +442,7 @@ func addToVmap(fm *provider, param flowType, vMap map[typeCode]int, rMap map[typ
406442

407443
func makeDebuggingProvider() (*provider, error) {
408444
d := newProvider(func() *Debugging { return nil }, -1, "Debugging")
445+
d.nonFinal = true
409446
d.cacheable = true
410447
d.mustCache = true
411448
d, err := characterizeFunc(d, charContext{inputsAreStatic: true})
@@ -415,3 +452,32 @@ func makeDebuggingProvider() (*provider, error) {
415452
d.isSynthetic = true
416453
return d, nil
417454
}
455+
456+
func makeUnusedInputProvider() (*provider, error) {
457+
d := newProvider(Unused{}, -1, "provide unused")
458+
d.nonFinal = true
459+
d.cacheable = true
460+
d.mustCache = true
461+
d.consumptionOptional = true
462+
d, err := characterizeFunc(d, charContext{inputsAreStatic: true})
463+
if err != nil {
464+
return nil, fmt.Errorf("internal error #328: problem with unused injectors: %w", err)
465+
}
466+
d.isSynthetic = true
467+
d.shun = true
468+
return d, nil
469+
}
470+
471+
func makeUnusedReturnsProvider() (*provider, error) {
472+
d := newProvider(func(inner func()) Unused { inner(); return Unused{} }, -1, "return unused")
473+
d.nonFinal = true
474+
d, err := characterizeFunc(d, charContext{inputsAreStatic: true})
475+
if err != nil {
476+
return nil, fmt.Errorf("internal error #278: problem with unused injectors: %w", err)
477+
}
478+
d.isSynthetic = true
479+
d.shun = true
480+
d.required = false
481+
d.consumptionOptional = true
482+
return d, nil
483+
}

bind_test.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,6 @@ func TestLiteral(t *testing.T) {
305305
counts := make(map[string]int)
306306
c := Sequence("TBF",
307307
s1("s1 value"),
308-
func() Provider {
309-
return Unused
310-
}(),
311308
Cacheable(func(s s1) s2 {
312309
counts["s2"]++
313310
assert.Equal(t, s1("s1 value"), s)

characterize.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ var (
140140
notLast = predicate("must not be last", func(a testArgs) bool { return !a.cc.isLast })
141141
unstaticOkay = predicate("is marked MustCache", func(a testArgs) bool { return !a.fm.mustCache })
142142
inStatic = predicate("is after invoke", func(a testArgs) bool { return a.cc.inputsAreStatic })
143-
hasOutputs = predicate("does not have outputs", func(a testArgs) bool { return a.t.NumOut() != 0 })
143+
hasOutputs = predicate("does not have outputs", func(a testArgs) bool { return len(stripUnused(typesOut(a.t))) != 0 })
144144
mustNotMemoize = predicate("is marked Memoized", func(a testArgs) bool { return !a.fm.memoize })
145145
markedMemoized = predicate("is not marked Memoized", func(a testArgs) bool { return a.fm.memoize })
146146
markedCacheable = predicate("is not marked Cacheable", func(a testArgs) bool { return a.fm.cacheable })

characterize_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,28 @@ var characterizeTests = []struct {
274274
returns(intTC, errorTC).
275275
returned(errorTC, intType3TC),
276276
},
277+
{
278+
"middleware func return int",
279+
func(func() intType3) {},
280+
wrapperFunc,
281+
lastF, staticT, panicF,
282+
params().
283+
input(noTypeCode).
284+
output().
285+
returns().
286+
returned(intType3TC),
287+
},
288+
{
289+
"middleware func return unused",
290+
func(func() Unused) {},
291+
wrapperFunc,
292+
lastF, staticT, panicF,
293+
params().
294+
input(noTypeCode).
295+
output().
296+
returns().
297+
returned(unusedTypeCode),
298+
},
277299
{
278300
"simple middleware regression",
279301
func(i func() error, w http.ResponseWriter) {},

flows.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,37 @@ func (c Collection) UpFlows() ([]reflect.Type, []reflect.Type) {
184184
return fm.UpFlows()
185185
})
186186
}
187+
188+
// stirpUnused redacts unusedType from lists
189+
func stripUnused(list []reflect.Type) []reflect.Type {
190+
for i, t := range list {
191+
if t == unusedType {
192+
n := make([]reflect.Type, i, len(list)-1)
193+
copy(n, list[:i])
194+
for _, t := range list[i+1:] {
195+
if t != unusedType {
196+
n = append(n, t)
197+
}
198+
}
199+
return n
200+
}
201+
}
202+
return list
203+
}
204+
205+
// stirpUnusedCode redacts unusedTypeCode from lists
206+
func stripUnusedCodes(list []typeCode) []typeCode {
207+
for i, t := range list {
208+
if t == unusedTypeCode {
209+
n := make([]typeCode, i, len(list)-1)
210+
copy(n, list[:i])
211+
for _, t := range list[i+1:] {
212+
if t != unusedTypeCode {
213+
n = append(n, t)
214+
}
215+
}
216+
return n
217+
}
218+
}
219+
return list
220+
}

include.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func computeDependenciesAndInclusion(funcs []*provider, initF *provider) ([]*pro
8888
fm.whyIncluded = "required"
8989
} else if fm.desired {
9090
fm.whyIncluded = "desired"
91-
} else if fm.flows[outputParams] != nil && len(fm.flows[outputParams]) == 0 {
91+
} else if fm.flows[outputParams] != nil && len(stripUnusedCodes(fm.flows[outputParams])) == 0 {
9292
fm.whyIncluded = "auto-desired (injector with no outputs)"
9393
if fm.cluster != 0 {
9494
fm.d.wantedInCluster = true
@@ -138,7 +138,7 @@ func computeDependenciesAndInclusion(funcs []*provider, initF *provider) ([]*pro
138138
}
139139

140140
debugln("eliminate unused providers")
141-
eliminateUnused(funcs) // xxx
141+
eliminateUnused(funcs)
142142

143143
tryWithout := func(without ...*provider) {
144144
if len(without) == 1 {
@@ -308,6 +308,9 @@ func checkFlows(funcs []*provider, numFuncs int, canRemoveDesired bool) error {
308308
}
309309
Param:
310310
for _, tc := range tclist {
311+
if tc == unusedTypeCode {
312+
continue
313+
}
311314
var extra string
312315
for _, p := range fm.d.usedByDetail[param][tc] {
313316
if p.include {

type_codes.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
"github.com/muir/reflectutils"
1010
)
1111

12-
type typeCode int
13-
type typeCodes []typeCode
12+
type (
13+
typeCode int
14+
typeCodes []typeCode
15+
)
1416

1517
var (
1618
typeCounter = 0
@@ -25,6 +27,8 @@ const noTypeExampleValue noType = false
2527

2628
var noTypeCode = getTypeCode(noTypeExampleValue)
2729

30+
var unusedTypeCode = getTypeCode(unusedType)
31+
2832
// noNoType filters out noTypeCode from an array of typeCode
2933
func noNoType(types []typeCode) []typeCode {
3034
found := -1

types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ type Debugging struct {
6666
Outer *Debugging
6767
}
6868

69+
// Unused is a special type: as an input, it will be provided automatically. If output from a
70+
// MustConsume provider, no consumer is needed. If an injector only returns Unused, then that
71+
// injector will be included in the chain, if possible, same as an injector that doesn't return
72+
// anything at all.
73+
type Unused struct{}
74+
6975
type classType int
7076

7177
const (
@@ -121,4 +127,6 @@ var (
121127
bypassDebugType = reflect.TypeOf((**bypassDebug)(nil)).Elem()
122128

123129
reflectiveFuncType = reflect.TypeOf((*func([]reflect.Type) []reflect.Type)(nil)).Elem()
130+
131+
unusedType = reflect.TypeOf((*Unused)(nil)).Elem()
124132
)

0 commit comments

Comments
 (0)