Skip to content

Commit 4541e57

Browse files
committed
Support simultanous name and group tags
As per Dig issue: #380 In order to support Fx feature requests uber-go/fx#998 uber-go/fx#1036 We need to be able to drop the restriction, both in terms of options dig.Name and dig.Group and dig.Out struct annotations on `name` and `group` being mutually exclusive. In a future PR, this can then be exploited to populate value group maps where the 'name' tag becomes the key of a map[string][T]
1 parent 7f9f0b8 commit 4541e57

File tree

5 files changed

+271
-74
lines changed

5 files changed

+271
-74
lines changed

decorate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ func findResultKeys(r resultList) ([]key, error) {
288288
keys = append(keys, key{t: innerResult.Type.Elem(), group: innerResult.Group})
289289
case resultObject:
290290
for _, f := range innerResult.Fields {
291-
q = append(q, f.Result)
291+
q = append(q, f.Results...)
292292
}
293293
case resultList:
294294
q = append(q, innerResult.Results...)

dig_test.go

Lines changed: 173 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,53 @@ func TestEndToEndSuccess(t *testing.T) {
749749
assert.ElementsMatch(t, actualStrs, expectedStrs, "list of strings provided must match")
750750
})
751751

752+
t.Run("multiple As with Group and Name", func(t *testing.T) {
753+
c := digtest.New(t)
754+
expectedNames := []string{"inst1", "inst2"}
755+
expectedStrs := []string{"foo", "bar"}
756+
for i, s := range expectedStrs {
757+
s := s
758+
c.RequireProvide(func() *bytes.Buffer {
759+
return bytes.NewBufferString(s)
760+
}, dig.Group("buffs"), dig.Name(expectedNames[i]),
761+
dig.As(new(io.Reader), new(io.Writer)))
762+
}
763+
764+
type in struct {
765+
dig.In
766+
767+
Reader1 io.Reader `name:"inst1"`
768+
Reader2 io.Reader `name:"inst2"`
769+
Readers []io.Reader `group:"buffs"`
770+
Writers []io.Writer `group:"buffs"`
771+
}
772+
773+
var actualStrs []string
774+
var actualStrsName []string
775+
776+
c.RequireInvoke(func(got in) {
777+
require.Len(t, got.Readers, 2)
778+
buf := make([]byte, 3)
779+
for i, r := range got.Readers {
780+
_, err := r.Read(buf)
781+
require.NoError(t, err)
782+
actualStrs = append(actualStrs, string(buf))
783+
// put the text back
784+
got.Writers[i].Write(buf)
785+
}
786+
_, err := got.Reader1.Read(buf)
787+
require.NoError(t, err)
788+
actualStrsName = append(actualStrsName, string(buf))
789+
_, err = got.Reader2.Read(buf)
790+
require.NoError(t, err)
791+
actualStrsName = append(actualStrsName, string(buf))
792+
require.Len(t, got.Writers, 2)
793+
})
794+
795+
assert.ElementsMatch(t, actualStrs, expectedStrs, "list of strings provided must match")
796+
assert.ElementsMatch(t, actualStrsName, expectedStrs, "names: list of strings provided must match")
797+
})
798+
752799
t.Run("As same interface", func(t *testing.T) {
753800
c := digtest.New(t)
754801
c.RequireProvide(func() io.Reader {
@@ -1098,6 +1145,48 @@ func TestGroups(t *testing.T) {
10981145
})
10991146
})
11001147

1148+
t.Run("values are provided; coexist with name", func(t *testing.T) {
1149+
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
1150+
1151+
type out struct {
1152+
dig.Out
1153+
1154+
Value int `group:"val"`
1155+
}
1156+
1157+
type out2 struct {
1158+
dig.Out
1159+
1160+
Value int `name:"inst1" group:"val"`
1161+
}
1162+
1163+
provide := func(i int) {
1164+
c.RequireProvide(func() out {
1165+
return out{Value: i}
1166+
})
1167+
}
1168+
1169+
provide(1)
1170+
provide(2)
1171+
provide(3)
1172+
1173+
c.RequireProvide(func() out2 {
1174+
return out2{Value: 4}
1175+
})
1176+
1177+
type in struct {
1178+
dig.In
1179+
1180+
SingleValue int `name:"inst1"`
1181+
Values []int `group:"val"`
1182+
}
1183+
1184+
c.RequireInvoke(func(i in) {
1185+
assert.Equal(t, []int{1, 2, 3, 4}, i.Values)
1186+
assert.Equal(t, 4, i.SingleValue)
1187+
})
1188+
})
1189+
11011190
t.Run("groups are provided via option", func(t *testing.T) {
11021191
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
11031192

@@ -1122,6 +1211,36 @@ func TestGroups(t *testing.T) {
11221211
})
11231212
})
11241213

1214+
t.Run("groups are provided via option; coexist with name", func(t *testing.T) {
1215+
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
1216+
1217+
provide := func(i int) {
1218+
c.RequireProvide(func() int {
1219+
return i
1220+
}, dig.Group("val"))
1221+
}
1222+
1223+
provide(1)
1224+
provide(2)
1225+
provide(3)
1226+
1227+
c.RequireProvide(func() int {
1228+
return 4
1229+
}, dig.Group("val"), dig.Name("inst1"))
1230+
1231+
type in struct {
1232+
dig.In
1233+
1234+
SingleValue int `name:"inst1"`
1235+
Values []int `group:"val"`
1236+
}
1237+
1238+
c.RequireInvoke(func(i in) {
1239+
assert.Equal(t, []int{1, 2, 3, 4}, i.Values)
1240+
assert.Equal(t, 4, i.SingleValue)
1241+
})
1242+
})
1243+
11251244
t.Run("different types may be grouped", func(t *testing.T) {
11261245
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
11271246

@@ -1998,21 +2117,6 @@ func TestAsExpectingOriginalType(t *testing.T) {
19982117
})
19992118
}
20002119

2001-
func TestProvideIncompatibleOptions(t *testing.T) {
2002-
t.Parallel()
2003-
2004-
t.Run("group and name", func(t *testing.T) {
2005-
c := digtest.New(t)
2006-
err := c.Provide(func() io.Reader {
2007-
t.Fatal("this function must not be called")
2008-
return nil
2009-
}, dig.Group("foo"), dig.Name("bar"))
2010-
require.Error(t, err)
2011-
assert.Contains(t, err.Error(), "cannot use named values with value groups: "+
2012-
`name:"bar" provided with group:"foo"`)
2013-
})
2014-
}
2015-
20162120
type testStruct struct{}
20172121

20182122
func (testStruct) TestMethod(x int) float64 { return float64(x) }
@@ -2559,6 +2663,60 @@ func testProvideFailures(t *testing.T, dryRun bool) {
25592663
)
25602664
})
25612665

2666+
t.Run("provide multiple instances with the same name and same group", func(t *testing.T) {
2667+
c := digtest.New(t, dig.DryRun(dryRun))
2668+
type A struct{}
2669+
type ret1 struct {
2670+
dig.Out
2671+
*A `name:"foo" group:"foos"`
2672+
}
2673+
type ret2 struct {
2674+
dig.Out
2675+
*A `name:"foo" group:"foos"`
2676+
}
2677+
c.RequireProvide(func() ret1 {
2678+
return ret1{A: &A{}}
2679+
})
2680+
2681+
err := c.Provide(func() ret2 {
2682+
return ret2{A: &A{}}
2683+
})
2684+
require.Error(t, err, "expected error on the second provide")
2685+
dig.AssertErrorMatches(t, err,
2686+
`cannot provide function "go.uber.org/dig_test".testProvideFailures\S+`,
2687+
`dig_test.go:\d+`, // file:line
2688+
`cannot provide \*dig_test.A\[name="foo"\] from \[0\].A:`,
2689+
`already provided by "go.uber.org/dig_test".testProvideFailures\S+`,
2690+
)
2691+
})
2692+
2693+
t.Run("provide multiple instances with the same name but different group", func(t *testing.T) {
2694+
c := digtest.New(t, dig.DryRun(dryRun))
2695+
type A struct{}
2696+
type ret1 struct {
2697+
dig.Out
2698+
*A `name:"foo" group:"foos"`
2699+
}
2700+
type ret2 struct {
2701+
dig.Out
2702+
*A `name:"foo" group:"foosss"`
2703+
}
2704+
c.RequireProvide(func() ret1 {
2705+
return ret1{A: &A{}}
2706+
})
2707+
2708+
err := c.Provide(func() ret2 {
2709+
return ret2{A: &A{}}
2710+
})
2711+
require.Error(t, err, "expected error on the second provide")
2712+
dig.AssertErrorMatches(t, err,
2713+
`cannot provide function "go.uber.org/dig_test".testProvideFailures\S+`,
2714+
`dig_test.go:\d+`, // file:line
2715+
`cannot provide \*dig_test.A\[name="foo"\] from \[0\].A:`,
2716+
`already provided by "go.uber.org/dig_test".testProvideFailures\S+`,
2717+
)
2718+
})
2719+
25622720
t.Run("out with unexported field should error", func(t *testing.T) {
25632721
c := digtest.New(t, dig.DryRun(dryRun))
25642722

provide.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,6 @@ type provideOptions struct {
4646
}
4747

4848
func (o *provideOptions) Validate() error {
49-
if len(o.Group) > 0 {
50-
if len(o.Name) > 0 {
51-
return newErrInvalidInput(
52-
fmt.Sprintf("cannot use named values with value groups: name:%q provided with group:%q", o.Name, o.Group), nil)
53-
}
54-
}
5549

5650
// Names must be representable inside a backquoted string. The only
5751
// limitation for raw string literals as per

0 commit comments

Comments
 (0)