Skip to content

Commit 48e086f

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 7709124 commit 48e086f

File tree

5 files changed

+332
-74
lines changed

5 files changed

+332
-74
lines changed

decorate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ func findResultKeys(r resultList) ([]key, error) {
315315
keys = append(keys, key{t: innerResult.Type.Elem(), group: innerResult.Group})
316316
case resultObject:
317317
for _, f := range innerResult.Fields {
318-
q = append(q, f.Result)
318+
q = append(q, f.Results...)
319319
}
320320
case resultList:
321321
q = append(q, innerResult.Results...)

dig_test.go

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

799+
t.Run("multiple As with Group and Name", func(t *testing.T) {
800+
c := digtest.New(t)
801+
expectedNames := []string{"inst1", "inst2"}
802+
expectedStrs := []string{"foo", "bar"}
803+
for i, s := range expectedStrs {
804+
s := s
805+
c.RequireProvide(func() *bytes.Buffer {
806+
return bytes.NewBufferString(s)
807+
}, dig.Group("buffs"), dig.Name(expectedNames[i]),
808+
dig.As(new(io.Reader), new(io.Writer)))
809+
}
810+
811+
type in struct {
812+
dig.In
813+
814+
Reader1 io.Reader `name:"inst1"`
815+
Reader2 io.Reader `name:"inst2"`
816+
Readers []io.Reader `group:"buffs"`
817+
Writers []io.Writer `group:"buffs"`
818+
}
819+
820+
var actualStrs []string
821+
var actualStrsName []string
822+
823+
c.RequireInvoke(func(got in) {
824+
require.Len(t, got.Readers, 2)
825+
buf := make([]byte, 3)
826+
for i, r := range got.Readers {
827+
_, err := r.Read(buf)
828+
require.NoError(t, err)
829+
actualStrs = append(actualStrs, string(buf))
830+
// put the text back
831+
got.Writers[i].Write(buf)
832+
}
833+
_, err := got.Reader1.Read(buf)
834+
require.NoError(t, err)
835+
actualStrsName = append(actualStrsName, string(buf))
836+
_, err = got.Reader2.Read(buf)
837+
require.NoError(t, err)
838+
actualStrsName = append(actualStrsName, string(buf))
839+
require.Len(t, got.Writers, 2)
840+
})
841+
842+
assert.ElementsMatch(t, actualStrs, expectedStrs, "list of strings provided must match")
843+
assert.ElementsMatch(t, actualStrsName, expectedStrs, "names: list of strings provided must match")
844+
})
845+
799846
t.Run("As same interface", func(t *testing.T) {
800847
c := digtest.New(t)
801848
c.RequireProvide(func() io.Reader {
@@ -1145,6 +1192,48 @@ func TestGroups(t *testing.T) {
11451192
})
11461193
})
11471194

1195+
t.Run("values are provided; coexist with name", func(t *testing.T) {
1196+
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
1197+
1198+
type out struct {
1199+
dig.Out
1200+
1201+
Value int `group:"val"`
1202+
}
1203+
1204+
type out2 struct {
1205+
dig.Out
1206+
1207+
Value int `name:"inst1" group:"val"`
1208+
}
1209+
1210+
provide := func(i int) {
1211+
c.RequireProvide(func() out {
1212+
return out{Value: i}
1213+
})
1214+
}
1215+
1216+
provide(1)
1217+
provide(2)
1218+
provide(3)
1219+
1220+
c.RequireProvide(func() out2 {
1221+
return out2{Value: 4}
1222+
})
1223+
1224+
type in struct {
1225+
dig.In
1226+
1227+
SingleValue int `name:"inst1"`
1228+
Values []int `group:"val"`
1229+
}
1230+
1231+
c.RequireInvoke(func(i in) {
1232+
assert.Equal(t, []int{1, 2, 3, 4}, i.Values)
1233+
assert.Equal(t, 4, i.SingleValue)
1234+
})
1235+
})
1236+
11481237
t.Run("groups are provided via option", func(t *testing.T) {
11491238
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
11501239

@@ -1169,6 +1258,36 @@ func TestGroups(t *testing.T) {
11691258
})
11701259
})
11711260

1261+
t.Run("groups are provided via option; coexist with name", func(t *testing.T) {
1262+
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
1263+
1264+
provide := func(i int) {
1265+
c.RequireProvide(func() int {
1266+
return i
1267+
}, dig.Group("val"))
1268+
}
1269+
1270+
provide(1)
1271+
provide(2)
1272+
provide(3)
1273+
1274+
c.RequireProvide(func() int {
1275+
return 4
1276+
}, dig.Group("val"), dig.Name("inst1"))
1277+
1278+
type in struct {
1279+
dig.In
1280+
1281+
SingleValue int `name:"inst1"`
1282+
Values []int `group:"val"`
1283+
}
1284+
1285+
c.RequireInvoke(func(i in) {
1286+
assert.Equal(t, []int{1, 2, 3, 4}, i.Values)
1287+
assert.Equal(t, 4, i.SingleValue)
1288+
})
1289+
})
1290+
11721291
t.Run("different types may be grouped", func(t *testing.T) {
11731292
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
11741293

@@ -1476,6 +1595,44 @@ func TestGroups(t *testing.T) {
14761595
})
14771596
})
14781597

1598+
t.Run("flatten collects slices but also handles name", func(t *testing.T) {
1599+
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
1600+
1601+
type out1 struct {
1602+
dig.Out
1603+
1604+
Value []int `name:"foo1" group:"val,flatten"`
1605+
}
1606+
1607+
type out2 struct {
1608+
dig.Out
1609+
1610+
Value []int `name:"foo2" group:"val,flatten"`
1611+
}
1612+
1613+
c.RequireProvide(func() out1 {
1614+
return out1{Value: []int{1, 2}}
1615+
})
1616+
1617+
c.RequireProvide(func() out2 {
1618+
return out2{Value: []int{3, 4}}
1619+
})
1620+
1621+
type in struct {
1622+
dig.In
1623+
1624+
NotFlattenedSlice1 []int `name:"foo1"`
1625+
NotFlattenedSlice2 []int `name:"foo2"`
1626+
Values []int `group:"val"`
1627+
}
1628+
1629+
c.RequireInvoke(func(i in) {
1630+
assert.Equal(t, []int{2, 3, 4, 1}, i.Values)
1631+
assert.Equal(t, []int{1, 2}, i.NotFlattenedSlice1)
1632+
assert.Equal(t, []int{3, 4}, i.NotFlattenedSlice2)
1633+
})
1634+
})
1635+
14791636
t.Run("flatten via option", func(t *testing.T) {
14801637
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
14811638
c.RequireProvide(func() []int {
@@ -1493,6 +1650,31 @@ func TestGroups(t *testing.T) {
14931650
})
14941651
})
14951652

1653+
t.Run("flatten via option also handles name", func(t *testing.T) {
1654+
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
1655+
c.RequireProvide(func() []int {
1656+
return []int{1, 2}
1657+
}, dig.Group("val,flatten"), dig.Name("foo1"))
1658+
1659+
c.RequireProvide(func() []int {
1660+
return []int{3}
1661+
}, dig.Group("val,flatten"), dig.Name("foo2"))
1662+
1663+
type in struct {
1664+
dig.In
1665+
1666+
NotFlattenedSlice1 []int `name:"foo1"`
1667+
NotFlattenedSlice2 []int `name:"foo2"`
1668+
Values []int `group:"val"`
1669+
}
1670+
1671+
c.RequireInvoke(func(i in) {
1672+
assert.Equal(t, []int{2, 3, 1}, i.Values)
1673+
assert.Equal(t, []int{1, 2}, i.NotFlattenedSlice1)
1674+
assert.Equal(t, []int{3}, i.NotFlattenedSlice2)
1675+
})
1676+
})
1677+
14961678
t.Run("flatten via option error if not a slice", func(t *testing.T) {
14971679
c := digtest.New(t, dig.SetRand(rand.New(rand.NewSource(0))))
14981680
err := c.Provide(func() int { return 1 }, dig.Group("val,flatten"))
@@ -2219,21 +2401,6 @@ func TestAsExpectingOriginalType(t *testing.T) {
22192401
})
22202402
}
22212403

2222-
func TestProvideIncompatibleOptions(t *testing.T) {
2223-
t.Parallel()
2224-
2225-
t.Run("group and name", func(t *testing.T) {
2226-
c := digtest.New(t)
2227-
err := c.Provide(func() io.Reader {
2228-
t.Fatal("this function must not be called")
2229-
return nil
2230-
}, dig.Group("foo"), dig.Name("bar"))
2231-
require.Error(t, err)
2232-
assert.Contains(t, err.Error(), "cannot use named values with value groups: "+
2233-
`name:"bar" provided with group:"foo"`)
2234-
})
2235-
}
2236-
22372404
type testStruct struct{}
22382405

22392406
func (testStruct) TestMethod(x int) float64 { return float64(x) }
@@ -2780,6 +2947,60 @@ func testProvideFailures(t *testing.T, dryRun bool) {
27802947
)
27812948
})
27822949

2950+
t.Run("provide multiple instances with the same name and same group", func(t *testing.T) {
2951+
c := digtest.New(t, dig.DryRun(dryRun))
2952+
type A struct{}
2953+
type ret1 struct {
2954+
dig.Out
2955+
*A `name:"foo" group:"foos"`
2956+
}
2957+
type ret2 struct {
2958+
dig.Out
2959+
*A `name:"foo" group:"foos"`
2960+
}
2961+
c.RequireProvide(func() ret1 {
2962+
return ret1{A: &A{}}
2963+
})
2964+
2965+
err := c.Provide(func() ret2 {
2966+
return ret2{A: &A{}}
2967+
})
2968+
require.Error(t, err, "expected error on the second provide")
2969+
dig.AssertErrorMatches(t, err,
2970+
`cannot provide function "go.uber.org/dig_test".testProvideFailures\S+`,
2971+
`dig_test.go:\d+`, // file:line
2972+
`cannot provide \*dig_test.A\[name="foo"\] from \[0\].A:`,
2973+
`already provided by "go.uber.org/dig_test".testProvideFailures\S+`,
2974+
)
2975+
})
2976+
2977+
t.Run("provide multiple instances with the same name but different group", func(t *testing.T) {
2978+
c := digtest.New(t, dig.DryRun(dryRun))
2979+
type A struct{}
2980+
type ret1 struct {
2981+
dig.Out
2982+
*A `name:"foo" group:"foos"`
2983+
}
2984+
type ret2 struct {
2985+
dig.Out
2986+
*A `name:"foo" group:"foosss"`
2987+
}
2988+
c.RequireProvide(func() ret1 {
2989+
return ret1{A: &A{}}
2990+
})
2991+
2992+
err := c.Provide(func() ret2 {
2993+
return ret2{A: &A{}}
2994+
})
2995+
require.Error(t, err, "expected error on the second provide")
2996+
dig.AssertErrorMatches(t, err,
2997+
`cannot provide function "go.uber.org/dig_test".testProvideFailures\S+`,
2998+
`dig_test.go:\d+`, // file:line
2999+
`cannot provide \*dig_test.A\[name="foo"\] from \[0\].A:`,
3000+
`already provided by "go.uber.org/dig_test".testProvideFailures\S+`,
3001+
)
3002+
})
3003+
27833004
t.Run("out with unexported field should error", func(t *testing.T) {
27843005
c := digtest.New(t, dig.DryRun(dryRun))
27853006

provide.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,6 @@ type provideOptions struct {
4848
}
4949

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

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

0 commit comments

Comments
 (0)