Skip to content

Commit

Permalink
Track the key of any named group objects.
Browse files Browse the repository at this point in the history
As part of #380 we allowed names
and groups tags/options to co-exist to ultimately support Fx feature
request uber-go/fx#998.

We now intend to support Map value groups as per
uber-go/fx#1036. We will do this in 2 steps.

1. This PR will begin tracking any names passed into value groups with
out changing any external facing functionality.

2. a subsequent PR will exploit this structure to support Map value
groups.
  • Loading branch information
jquirke committed Mar 9, 2023
1 parent 5a5b50f commit e781757
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 34 deletions.
16 changes: 8 additions & 8 deletions constructor.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,15 @@ func (n *constructorNode) Call(c containerStore) (err error) {
// would be made to a containerWriter and defers them until Commit is called.
type stagingContainerWriter struct {
values map[key]reflect.Value
groups map[key][]reflect.Value
groups map[key][]keyedGroupValue
}

var _ containerWriter = (*stagingContainerWriter)(nil)

func newStagingContainerWriter() *stagingContainerWriter {
return &stagingContainerWriter{
values: make(map[key]reflect.Value),
groups: make(map[key][]reflect.Value),
groups: make(map[key][]keyedGroupValue),
}
}

Expand All @@ -201,12 +201,12 @@ func (sr *stagingContainerWriter) setDecoratedValue(_ string, _ reflect.Type, _
digerror.BugPanicf("stagingContainerWriter.setDecoratedValue must never be called")
}

func (sr *stagingContainerWriter) submitGroupedValue(group string, t reflect.Type, v reflect.Value) {
func (sr *stagingContainerWriter) submitGroupedValue(group, mapKey string, t reflect.Type, v reflect.Value) {
k := key{t: t, group: group}
sr.groups[k] = append(sr.groups[k], v)
sr.groups[k] = append(sr.groups[k], keyedGroupValue{key: mapKey, value: v})
}

func (sr *stagingContainerWriter) submitDecoratedGroupedValue(_ string, _ reflect.Type, _ reflect.Value) {
func (sr *stagingContainerWriter) submitDecoratedGroupedValue(_, _ string, _ reflect.Type, _ reflect.Value) {
digerror.BugPanicf("stagingContainerWriter.submitDecoratedGroupedValue must never be called")
}

Expand All @@ -216,9 +216,9 @@ func (sr *stagingContainerWriter) Commit(cw containerWriter) {
cw.setValue(k.name, k.t, v)
}

for k, vs := range sr.groups {
for _, v := range vs {
cw.submitGroupedValue(k.group, k.t, v)
for k, kgvs := range sr.groups {
for _, kgv := range kgvs {
cw.submitGroupedValue(k.group, kgv.key, k.t, kgv.value)
}
}
}
14 changes: 7 additions & 7 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ type containerWriter interface {
setDecoratedValue(name string, t reflect.Type, v reflect.Value)

// submitGroupedValue submits a value to the value group with the provided
// name.
submitGroupedValue(name string, t reflect.Type, v reflect.Value)
// name and optional map key.
submitGroupedValue(name, mapKey string, t reflect.Type, v reflect.Value)

// submitDecoratedGroupedValue submits a decorated value to the value group
// with the provided name.
submitDecoratedGroupedValue(name string, t reflect.Type, v reflect.Value)
// with the provided name and optional map key.
submitDecoratedGroupedValue(name, mapKey string, t reflect.Type, v reflect.Value)
}

// containerStore provides access to the Container's underlying data store.
Expand All @@ -108,7 +108,7 @@ type containerStore interface {
// Retrieves all values for the provided group and type.
//
// The order in which the values are returned is undefined.
getValueGroup(name string, t reflect.Type) []reflect.Value
getValueGroup(name string, t reflect.Type) []keyedGroupValue

// Retrieves all decorated values for the provided group and type, if any.
getDecoratedValueGroup(name string, t reflect.Type) (reflect.Value, bool)
Expand Down Expand Up @@ -273,8 +273,8 @@ func (bs byTypeName) Swap(i int, j int) {
bs[i], bs[j] = bs[j], bs[i]
}

func shuffledCopy(rand *rand.Rand, items []reflect.Value) []reflect.Value {
newItems := make([]reflect.Value, len(items))
func shuffledCopy(rand *rand.Rand, items []keyedGroupValue) []keyedGroupValue {
newItems := make([]keyedGroupValue, len(items))
for i, j := range rand.Perm(len(items)) {
newItems[i] = items[j]
}
Expand Down
6 changes: 5 additions & 1 deletion param.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ func (pt paramGroupedSlice) Build(c containerStore) (reflect.Value, error) {
}

// Check if we have decorated values
// qjeremy(how to handle this with maps?)
if decoratedItems, ok := pt.getDecoratedValues(c); ok {
return decoratedItems, nil
}
Expand All @@ -645,7 +646,10 @@ func (pt paramGroupedSlice) Build(c containerStore) (reflect.Value, error) {
stores := c.storesToRoot()
result := reflect.MakeSlice(pt.Type, 0, itemCount)
for _, c := range stores {
result = reflect.Append(result, c.getValueGroup(pt.Group, pt.Type.Elem())...)
kgvs := c.getValueGroup(pt.Group, pt.Type.Elem())
for _, kgv := range kgvs {
result = reflect.Append(result, kgv.value)
}
}
return result, nil
}
Expand Down
14 changes: 10 additions & 4 deletions result.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,9 @@ type resultGrouped struct {
// Name of the group as specified in the `group:".."` tag.
Group string

// Key if a name tag or option was provided, for populating maps
Key string

// Type of value produced.
Type reflect.Type

Expand Down Expand Up @@ -527,8 +530,10 @@ func newResultGrouped(f reflect.StructField) (resultGrouped, error) {
if err != nil {
return resultGrouped{}, err
}
name := f.Tag.Get(_nameTag)
rg := resultGrouped{
Group: g.Name,
Key: name,
Flatten: g.Flatten,
Type: f.Type,
}
Expand All @@ -553,18 +558,19 @@ func newResultGrouped(f reflect.StructField) (resultGrouped, error) {
func (rt resultGrouped) Extract(cw containerWriter, decorated bool, v reflect.Value) {
// Decorated values are always flattened.
if !decorated && !rt.Flatten {
cw.submitGroupedValue(rt.Group, rt.Type, v)
cw.submitGroupedValue(rt.Group, rt.Key, rt.Type, v)
for _, asType := range rt.As {
cw.submitGroupedValue(rt.Group, asType, v)
cw.submitGroupedValue(rt.Group, rt.Key, asType, v)
}
return
}

if decorated {
cw.submitDecoratedGroupedValue(rt.Group, rt.Type, v)
cw.submitDecoratedGroupedValue(rt.Group, rt.Key, rt.Type, v)
return
}
// it's not possible to provide a key for the flattening case
for i := 0; i < v.Len(); i++ {
cw.submitGroupedValue(rt.Group, rt.Type, v.Index(i))
cw.submitGroupedValue(rt.Group, "", rt.Type, v.Index(i))
}
}
2 changes: 1 addition & 1 deletion result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func TestNewResultObject(t *testing.T) {
FieldName: "Writer",
FieldIndex: 1,
Results: []result{
resultGrouped{Group: "writers", Type: typeOfWriter},
resultGrouped{Group: "writers", Key: "writer1", Type: typeOfWriter},
resultSingle{Name: "writer1", Type: typeOfWriter},
},
},
Expand Down
31 changes: 18 additions & 13 deletions scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ type ScopeOption interface {
noScopeOption() //yet
}

type keyedGroupValue struct {
key string
value reflect.Value
}

// Scope is a scoped DAG of types and their dependencies.
// A Scope may also have one or more child Scopes that inherit
// from it.
Expand All @@ -61,10 +66,10 @@ type Scope struct {
values map[key]reflect.Value

// Values groups that generated directly in the Scope.
groups map[key][]reflect.Value
groups map[key][]keyedGroupValue

// Values groups that generated via decoraters in the Scope.
decoratedGroups map[key]reflect.Value
decoratedGroups map[key]keyedGroupValue

// Source of randomness.
rand *rand.Rand
Expand Down Expand Up @@ -98,8 +103,8 @@ func newScope() *Scope {
decorators: make(map[key]*decoratorNode),
values: make(map[key]reflect.Value),
decoratedValues: make(map[key]reflect.Value),
groups: make(map[key][]reflect.Value),
decoratedGroups: make(map[key]reflect.Value),
groups: make(map[key][]keyedGroupValue),
decoratedGroups: make(map[key]keyedGroupValue),
invokerFn: defaultInvoker,
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
}
Expand Down Expand Up @@ -190,25 +195,25 @@ func (s *Scope) setDecoratedValue(name string, t reflect.Type, v reflect.Value)
s.decoratedValues[key{name: name, t: t}] = v
}

func (s *Scope) getValueGroup(name string, t reflect.Type) []reflect.Value {
func (s *Scope) getValueGroup(name string, t reflect.Type) []keyedGroupValue {
items := s.groups[key{group: name, t: t}]
// shuffle the list so users don't rely on the ordering of grouped values
return shuffledCopy(s.rand, items)
}

func (s *Scope) getDecoratedValueGroup(name string, t reflect.Type) (reflect.Value, bool) {
items, ok := s.decoratedGroups[key{group: name, t: t}]
return items, ok
return items.value, ok
}

func (s *Scope) submitGroupedValue(name string, t reflect.Type, v reflect.Value) {
func (s *Scope) submitGroupedValue(name, mapKey string, t reflect.Type, v reflect.Value) {
k := key{group: name, t: t}
s.groups[k] = append(s.groups[k], v)
s.groups[k] = append(s.groups[k], keyedGroupValue{key: mapKey, value: v})
}

func (s *Scope) submitDecoratedGroupedValue(name string, t reflect.Type, v reflect.Value) {
func (s *Scope) submitDecoratedGroupedValue(name, mapKey string, t reflect.Type, v reflect.Value) {
k := key{group: name, t: t}
s.decoratedGroups[k] = v
s.decoratedGroups[k] = keyedGroupValue{key: mapKey, value: v}
}

func (s *Scope) getValueProviders(name string, t reflect.Type) []provider {
Expand Down Expand Up @@ -310,9 +315,9 @@ func (s *Scope) String() string {
for k, v := range s.values {
fmt.Fprintln(b, "\t", k, "=>", v)
}
for k, vs := range s.groups {
for _, v := range vs {
fmt.Fprintln(b, "\t", k, "=>", v)
for k, kgvs := range s.groups {
for _, kgv := range kgvs {
fmt.Fprintln(b, "\t", k, "=>", kgv.value)
}
}
fmt.Fprintln(b, "}")
Expand Down

0 comments on commit e781757

Please sign in to comment.