@@ -38,10 +38,12 @@ import (
38
38
// paramSingle An explicitly requested type.
39
39
// paramObject dig.In struct where each field in the struct can be another
40
40
// param.
41
- // paramGroupedSlice
42
- // A slice consuming a value group. This will receive all
41
+ // paramGroupedCollection
42
+ // A slice or map consuming a value group. This will receive all
43
43
// values produced with a `group:".."` tag with the same name
44
- // as a slice.
44
+ // as a slice or map. For a map, every value produced with the
45
+ // same group name MUST have a name which will form the map key.
46
+
45
47
type param interface {
46
48
fmt.Stringer
47
49
59
61
_ param = paramSingle {}
60
62
_ param = paramObject {}
61
63
_ param = paramList {}
62
- _ param = paramGroupedSlice {}
64
+ _ param = paramGroupedCollection {}
63
65
)
64
66
65
67
// newParam builds a param from the given type. If the provided type is a
@@ -342,7 +344,7 @@ func getParamOrder(gh *graphHolder, param param) []int {
342
344
for _ , provider := range providers {
343
345
orders = append (orders , provider .Order (gh .s ))
344
346
}
345
- case paramGroupedSlice :
347
+ case paramGroupedCollection :
346
348
// value group parameters have nodes of their own.
347
349
// We can directly return that here.
348
350
orders = append (orders , p .orders [gh .s ])
@@ -401,7 +403,7 @@ func (po paramObject) Build(c containerStore) (reflect.Value, error) {
401
403
var softGroupsQueue []paramObjectField
402
404
var fields []paramObjectField
403
405
for _ , f := range po .Fields {
404
- if p , ok := f .Param .(paramGroupedSlice ); ok && p .Soft {
406
+ if p , ok := f .Param .(paramGroupedCollection ); ok && p .Soft {
405
407
softGroupsQueue = append (softGroupsQueue , f )
406
408
continue
407
409
}
@@ -451,7 +453,7 @@ func newParamObjectField(idx int, f reflect.StructField, c containerStore) (para
451
453
452
454
case f .Tag .Get (_groupTag ) != "" :
453
455
var err error
454
- p , err = newParamGroupedSlice (f , c )
456
+ p , err = newParamGroupedCollection (f , c )
455
457
if err != nil {
456
458
return pof , err
457
459
}
@@ -488,29 +490,31 @@ func (pof paramObjectField) Build(c containerStore) (reflect.Value, error) {
488
490
return v , nil
489
491
}
490
492
491
- // paramGroupedSlice is a param which produces a slice of values with the same
493
+ // paramGroupedCollection is a param which produces a slice or map of values with the same
492
494
// group name.
493
- type paramGroupedSlice struct {
495
+ type paramGroupedCollection struct {
494
496
// Name of the group as specified in the `group:".."` tag.
495
497
Group string
496
498
497
- // Type of the slice.
499
+ // Type of the map or slice.
498
500
Type reflect.Type
499
501
500
502
// Soft is used to denote a soft dependency between this param and its
501
503
// constructors, if it's true its constructors are only called if they
502
504
// provide another value requested in the graph
503
505
Soft bool
504
506
507
+ isMap bool
505
508
orders map [* Scope ]int
506
509
}
507
510
508
- func (pt paramGroupedSlice ) String () string {
511
+ func (pt paramGroupedCollection ) String () string {
509
512
// io.Reader[group="foo"] refers to a group of io.Readers called 'foo'
510
513
return fmt .Sprintf ("%v[group=%q]" , pt .Type .Elem (), pt .Group )
514
+ // JQTODO, different string for map
511
515
}
512
516
513
- func (pt paramGroupedSlice ) DotParam () []* dot.Param {
517
+ func (pt paramGroupedCollection ) DotParam () []* dot.Param {
514
518
return []* dot.Param {
515
519
{
516
520
Node : & dot.Node {
@@ -521,28 +525,31 @@ func (pt paramGroupedSlice) DotParam() []*dot.Param {
521
525
}
522
526
}
523
527
524
- // newParamGroupedSlice builds a paramGroupedSlice from the provided type with
528
+ // newParamGroupedCollection builds a paramGroupedCollection from the provided type with
525
529
// the given name.
526
530
//
527
- // The type MUST be a slice type.
528
- func newParamGroupedSlice (f reflect.StructField , c containerStore ) (paramGroupedSlice , error ) {
531
+ // The type MUST be a slice or map[string]T type.
532
+ func newParamGroupedCollection (f reflect.StructField , c containerStore ) (paramGroupedCollection , error ) {
529
533
g , err := parseGroupString (f .Tag .Get (_groupTag ))
530
534
if err != nil {
531
- return paramGroupedSlice {}, err
535
+ return paramGroupedCollection {}, err
532
536
}
533
- pg := paramGroupedSlice {
537
+ isMap := f .Type .Kind () == reflect .Map && f .Type .Key ().Kind () == reflect .String
538
+ isSlice := f .Type .Kind () == reflect .Slice
539
+ pg := paramGroupedCollection {
534
540
Group : g .Name ,
535
541
Type : f .Type ,
542
+ isMap : isMap ,
536
543
orders : make (map [* Scope ]int ),
537
544
Soft : g .Soft ,
538
545
}
539
546
540
547
name := f .Tag .Get (_nameTag )
541
548
optional , _ := isFieldOptional (f )
542
549
switch {
543
- case f . Type . Kind () != reflect . Slice :
550
+ case ! isMap && ! isSlice :
544
551
return pg , newErrInvalidInput (
545
- fmt .Sprintf ("value groups may be consumed as slices only: field %q (%v) is not a slice" , f .Name , f .Type ), nil )
552
+ fmt .Sprintf ("value groups may be consumed as slices or string-keyed maps only: field %q (%v) is not a slice or string-keyed map " , f .Name , f .Type ), nil )
546
553
case g .Flatten :
547
554
return pg , newErrInvalidInput (
548
555
fmt .Sprintf ("cannot use flatten in parameter value groups: field %q (%v) specifies flatten" , f .Name , f .Type ), nil )
@@ -560,7 +567,7 @@ func newParamGroupedSlice(f reflect.StructField, c containerStore) (paramGrouped
560
567
// any of the parent Scopes. In the case where there are multiple scopes that
561
568
// are decorating the same type, the closest scope in effect will be replacing
562
569
// any decorated value groups provided in further scopes.
563
- func (pt paramGroupedSlice ) getDecoratedValues (c containerStore ) (reflect.Value , bool ) {
570
+ func (pt paramGroupedCollection ) getDecoratedValues (c containerStore ) (reflect.Value , bool ) {
564
571
for _ , c := range c .storesToRoot () {
565
572
if items , ok := c .getDecoratedValueGroup (pt .Group , pt .Type ); ok {
566
573
return items , true
@@ -575,7 +582,7 @@ func (pt paramGroupedSlice) getDecoratedValues(c containerStore) (reflect.Value,
575
582
// The order in which the decorators are invoked is from the top level scope to
576
583
// the current scope, to account for decorators that decorate values that were
577
584
// already decorated.
578
- func (pt paramGroupedSlice ) callGroupDecorators (c containerStore ) error {
585
+ func (pt paramGroupedCollection ) callGroupDecorators (c containerStore ) error {
579
586
stores := c .storesToRoot ()
580
587
for i := len (stores ) - 1 ; i >= 0 ; i -- {
581
588
c := stores [i ]
@@ -600,7 +607,7 @@ func (pt paramGroupedSlice) callGroupDecorators(c containerStore) error {
600
607
// search the given container and its parent for matching group providers and
601
608
// call them to commit values. If an error is encountered, return the number
602
609
// of providers called and a non-nil error from the first provided.
603
- func (pt paramGroupedSlice ) callGroupProviders (c containerStore ) (int , error ) {
610
+ func (pt paramGroupedCollection ) callGroupProviders (c containerStore ) (int , error ) {
604
611
itemCount := 0
605
612
for _ , c := range c .storesToRoot () {
606
613
providers := c .getGroupProviders (pt .Group , pt .Type .Elem ())
@@ -618,7 +625,7 @@ func (pt paramGroupedSlice) callGroupProviders(c containerStore) (int, error) {
618
625
return itemCount , nil
619
626
}
620
627
621
- func (pt paramGroupedSlice ) Build (c containerStore ) (reflect.Value , error ) {
628
+ func (pt paramGroupedCollection ) Build (c containerStore ) (reflect.Value , error ) {
622
629
// do not call this if we are already inside a decorator since
623
630
// it will result in an infinite recursion. (i.e. decorate -> params.BuildList() -> Decorate -> params.BuildList...)
624
631
// this is safe since a value can be decorated at most once in a given scope.
@@ -644,6 +651,22 @@ func (pt paramGroupedSlice) Build(c containerStore) (reflect.Value, error) {
644
651
}
645
652
646
653
stores := c .storesToRoot ()
654
+ if pt .isMap {
655
+ result := reflect .MakeMapWithSize (pt .Type , itemCount )
656
+ for _ , c := range stores {
657
+ kgvs := c .getValueGroup (pt .Group , pt .Type .Elem ())
658
+ for _ , kgv := range kgvs {
659
+ if kgv .key == "" {
660
+ return _noValue , newErrInvalidInput (
661
+ fmt .Sprintf ("every entry in a map value groups must have a name, group \" %v\" is missing a name" , pt .Group ),
662
+ nil ,
663
+ )
664
+ }
665
+ result .SetMapIndex (reflect .ValueOf (kgv .key ), kgv .value )
666
+ }
667
+ }
668
+ return result , nil
669
+ }
647
670
result := reflect .MakeSlice (pt .Type , 0 , itemCount )
648
671
for _ , c := range stores {
649
672
kgvs := c .getValueGroup (pt .Group , pt .Type .Elem ())
0 commit comments