 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			666 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			666 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2021 Uber Technologies, Inc.
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| // of this software and associated documentation files (the "Software"), to deal
 | |
| // in the Software without restriction, including without limitation the rights
 | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| // copies of the Software, and to permit persons to whom the Software is
 | |
| // furnished to do so, subject to the following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included in
 | |
| // all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
| // THE SOFTWARE.
 | |
| 
 | |
| package dig
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| 
 | |
| 	"go.uber.org/dig/internal/digreflect"
 | |
| 	"go.uber.org/dig/internal/dot"
 | |
| 	"go.uber.org/dig/internal/graph"
 | |
| )
 | |
| 
 | |
| // A ProvideOption modifies the default behavior of Provide.
 | |
| type ProvideOption interface {
 | |
| 	applyProvideOption(*provideOptions)
 | |
| }
 | |
| 
 | |
| type provideOptions struct {
 | |
| 	Name     string
 | |
| 	Group    string
 | |
| 	Info     *ProvideInfo
 | |
| 	As       []interface{}
 | |
| 	Location *digreflect.Func
 | |
| 	Exported bool
 | |
| 	Callback Callback
 | |
| }
 | |
| 
 | |
| func (o *provideOptions) Validate() error {
 | |
| 	if len(o.Group) > 0 {
 | |
| 		if len(o.Name) > 0 {
 | |
| 			return newErrInvalidInput(
 | |
| 				fmt.Sprintf("cannot use named values with value groups: name:%q provided with group:%q", o.Name, o.Group), nil)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Names must be representable inside a backquoted string. The only
 | |
| 	// limitation for raw string literals as per
 | |
| 	// https://golang.org/ref/spec#raw_string_lit is that they cannot contain
 | |
| 	// backquotes.
 | |
| 	if strings.ContainsRune(o.Name, '`') {
 | |
| 		return newErrInvalidInput(
 | |
| 			fmt.Sprintf("invalid dig.Name(%q): names cannot contain backquotes", o.Name), nil)
 | |
| 	}
 | |
| 	if strings.ContainsRune(o.Group, '`') {
 | |
| 		return newErrInvalidInput(
 | |
| 			fmt.Sprintf("invalid dig.Group(%q): group names cannot contain backquotes", o.Group), nil)
 | |
| 	}
 | |
| 
 | |
| 	for _, i := range o.As {
 | |
| 		t := reflect.TypeOf(i)
 | |
| 
 | |
| 		if t == nil {
 | |
| 			return newErrInvalidInput("invalid dig.As(nil): argument must be a pointer to an interface", nil)
 | |
| 		}
 | |
| 
 | |
| 		if t.Kind() != reflect.Ptr {
 | |
| 			return newErrInvalidInput(
 | |
| 				fmt.Sprintf("invalid dig.As(%v): argument must be a pointer to an interface", t), nil)
 | |
| 		}
 | |
| 
 | |
| 		pointingTo := t.Elem()
 | |
| 		if pointingTo.Kind() != reflect.Interface {
 | |
| 			return newErrInvalidInput(
 | |
| 				fmt.Sprintf("invalid dig.As(*%v): argument must be a pointer to an interface", pointingTo), nil)
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Name is a ProvideOption that specifies that all values produced by a
 | |
| // constructor should have the given name. See also the package documentation
 | |
| // about Named Values.
 | |
| //
 | |
| // Given,
 | |
| //
 | |
| //	func NewReadOnlyConnection(...) (*Connection, error)
 | |
| //	func NewReadWriteConnection(...) (*Connection, error)
 | |
| //
 | |
| // The following will provide two connections to the container: one under the
 | |
| // name "ro" and the other under the name "rw".
 | |
| //
 | |
| //	c.Provide(NewReadOnlyConnection, dig.Name("ro"))
 | |
| //	c.Provide(NewReadWriteConnection, dig.Name("rw"))
 | |
| //
 | |
| // This option cannot be provided for constructors which produce result
 | |
| // objects.
 | |
| func Name(name string) ProvideOption {
 | |
| 	return provideNameOption(name)
 | |
| }
 | |
| 
 | |
| type provideNameOption string
 | |
| 
 | |
| func (o provideNameOption) String() string {
 | |
| 	return fmt.Sprintf("Name(%q)", string(o))
 | |
| }
 | |
| 
 | |
| func (o provideNameOption) applyProvideOption(opt *provideOptions) {
 | |
| 	opt.Name = string(o)
 | |
| }
 | |
| 
 | |
| // Group is a ProvideOption that specifies that all values produced by a
 | |
| // constructor should be added to the specified group. See also the package
 | |
| // documentation about Value Groups.
 | |
| //
 | |
| // This option cannot be provided for constructors which produce result
 | |
| // objects.
 | |
| func Group(group string) ProvideOption {
 | |
| 	return provideGroupOption(group)
 | |
| }
 | |
| 
 | |
| type provideGroupOption string
 | |
| 
 | |
| func (o provideGroupOption) String() string {
 | |
| 	return fmt.Sprintf("Group(%q)", string(o))
 | |
| }
 | |
| 
 | |
| func (o provideGroupOption) applyProvideOption(opt *provideOptions) {
 | |
| 	opt.Group = string(o)
 | |
| }
 | |
| 
 | |
| // ID is a unique integer representing the constructor node in the dependency graph.
 | |
| type ID int
 | |
| 
 | |
| // ProvideInfo provides information about the constructor's inputs and outputs
 | |
| // types as strings, as well as the ID of the constructor supplied to the Container.
 | |
| // It contains ID for the constructor, as well as slices of Input and Output types,
 | |
| // which are Stringers that report the types of the parameters and results respectively.
 | |
| type ProvideInfo struct {
 | |
| 	ID      ID
 | |
| 	Inputs  []*Input
 | |
| 	Outputs []*Output
 | |
| }
 | |
| 
 | |
| // Input contains information on an input parameter of a function.
 | |
| type Input struct {
 | |
| 	t           reflect.Type
 | |
| 	optional    bool
 | |
| 	name, group string
 | |
| }
 | |
| 
 | |
| func (i *Input) String() string {
 | |
| 	toks := make([]string, 0, 3)
 | |
| 	t := i.t.String()
 | |
| 	if i.optional {
 | |
| 		toks = append(toks, "optional")
 | |
| 	}
 | |
| 	if i.name != "" {
 | |
| 		toks = append(toks, fmt.Sprintf("name = %q", i.name))
 | |
| 	}
 | |
| 	if i.group != "" {
 | |
| 		toks = append(toks, fmt.Sprintf("group = %q", i.group))
 | |
| 	}
 | |
| 
 | |
| 	if len(toks) == 0 {
 | |
| 		return t
 | |
| 	}
 | |
| 	return fmt.Sprintf("%v[%v]", t, strings.Join(toks, ", "))
 | |
| }
 | |
| 
 | |
| // Output contains information on an output produced by a function.
 | |
| type Output struct {
 | |
| 	t           reflect.Type
 | |
| 	name, group string
 | |
| }
 | |
| 
 | |
| func (o *Output) String() string {
 | |
| 	toks := make([]string, 0, 2)
 | |
| 	t := o.t.String()
 | |
| 	if o.name != "" {
 | |
| 		toks = append(toks, fmt.Sprintf("name = %q", o.name))
 | |
| 	}
 | |
| 	if o.group != "" {
 | |
| 		toks = append(toks, fmt.Sprintf("group = %q", o.group))
 | |
| 	}
 | |
| 
 | |
| 	if len(toks) == 0 {
 | |
| 		return t
 | |
| 	}
 | |
| 	return fmt.Sprintf("%v[%v]", t, strings.Join(toks, ", "))
 | |
| }
 | |
| 
 | |
| // FillProvideInfo is a ProvideOption that writes info on what Dig was able to get
 | |
| // out of the provided constructor into the provided ProvideInfo.
 | |
| func FillProvideInfo(info *ProvideInfo) ProvideOption {
 | |
| 	return fillProvideInfoOption{info: info}
 | |
| }
 | |
| 
 | |
| type fillProvideInfoOption struct{ info *ProvideInfo }
 | |
| 
 | |
| func (o fillProvideInfoOption) String() string {
 | |
| 	return fmt.Sprintf("FillProvideInfo(%p)", o.info)
 | |
| }
 | |
| 
 | |
| func (o fillProvideInfoOption) applyProvideOption(opts *provideOptions) {
 | |
| 	opts.Info = o.info
 | |
| }
 | |
| 
 | |
| // As is a ProvideOption that specifies that the value produced by the
 | |
| // constructor implements one or more other interfaces and is provided
 | |
| // to the container as those interfaces.
 | |
| //
 | |
| // As expects one or more pointers to the implemented interfaces. Values
 | |
| // produced by constructors will be then available in the container as
 | |
| // implementations of all of those interfaces, but not as the value itself.
 | |
| //
 | |
| // For example, the following will make io.Reader and io.Writer available
 | |
| // in the container, but not buffer.
 | |
| //
 | |
| //	c.Provide(newBuffer, dig.As(new(io.Reader), new(io.Writer)))
 | |
| //
 | |
| // That is, the above is equivalent to the following.
 | |
| //
 | |
| //	c.Provide(func(...) (io.Reader, io.Writer) {
 | |
| //	  b := newBuffer(...)
 | |
| //	  return b, b
 | |
| //	})
 | |
| //
 | |
| // If used with dig.Name, the type produced by the constructor and the types
 | |
| // specified with dig.As will all use the same name. For example,
 | |
| //
 | |
| //	c.Provide(newFile, dig.As(new(io.Reader)), dig.Name("temp"))
 | |
| //
 | |
| // The above is equivalent to the following.
 | |
| //
 | |
| //	type Result struct {
 | |
| //	  dig.Out
 | |
| //
 | |
| //	  Reader io.Reader `name:"temp"`
 | |
| //	}
 | |
| //
 | |
| //	c.Provide(func(...) Result {
 | |
| //	  f := newFile(...)
 | |
| //	  return Result{
 | |
| //	    Reader: f,
 | |
| //	  }
 | |
| //	})
 | |
| //
 | |
| // This option cannot be provided for constructors which produce result
 | |
| // objects.
 | |
| func As(i ...interface{}) ProvideOption {
 | |
| 	return provideAsOption(i)
 | |
| }
 | |
| 
 | |
| type provideAsOption []interface{}
 | |
| 
 | |
| func (o provideAsOption) String() string {
 | |
| 	buf := bytes.NewBufferString("As(")
 | |
| 	for i, iface := range o {
 | |
| 		if i > 0 {
 | |
| 			buf.WriteString(", ")
 | |
| 		}
 | |
| 		buf.WriteString(reflect.TypeOf(iface).Elem().String())
 | |
| 	}
 | |
| 	buf.WriteString(")")
 | |
| 	return buf.String()
 | |
| }
 | |
| 
 | |
| func (o provideAsOption) applyProvideOption(opts *provideOptions) {
 | |
| 	opts.As = append(opts.As, o...)
 | |
| }
 | |
| 
 | |
| // LocationForPC is a ProvideOption which specifies an alternate function program
 | |
| // counter address to be used for debug information. The package, name, file and
 | |
| // line number of this alternate function address will be used in error messages
 | |
| // and DOT graphs. This option is intended to be used with functions created
 | |
| // with the reflect.MakeFunc method whose error messages are otherwise hard to
 | |
| // understand
 | |
| func LocationForPC(pc uintptr) ProvideOption {
 | |
| 	return provideLocationOption{
 | |
| 		loc: digreflect.InspectFuncPC(pc),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type provideLocationOption struct{ loc *digreflect.Func }
 | |
| 
 | |
| func (o provideLocationOption) String() string {
 | |
| 	return fmt.Sprintf("LocationForPC(%v)", o.loc)
 | |
| }
 | |
| 
 | |
| func (o provideLocationOption) applyProvideOption(opts *provideOptions) {
 | |
| 	opts.Location = o.loc
 | |
| }
 | |
| 
 | |
| // Export is a ProvideOption which specifies that the provided function should
 | |
| // be made available to all Scopes available in the application, regardless
 | |
| // of which Scope it was provided from. By default, it is false.
 | |
| //
 | |
| // For example,
 | |
| //
 | |
| //	c := New()
 | |
| //	s1 := c.Scope("child 1")
 | |
| //	s2:= c.Scope("child 2")
 | |
| //	s1.Provide(func() *bytes.Buffer { ... })
 | |
| //
 | |
| // does not allow the constructor returning *bytes.Buffer to be made available to
 | |
| // the root Container c or its sibling Scope s2.
 | |
| //
 | |
| // With Export, you can make this constructor available to all the Scopes:
 | |
| //
 | |
| //	s1.Provide(func() *bytes.Buffer { ... }, Export(true))
 | |
| func Export(export bool) ProvideOption {
 | |
| 	return provideExportOption{exported: export}
 | |
| }
 | |
| 
 | |
| type provideExportOption struct{ exported bool }
 | |
| 
 | |
| func (o provideExportOption) String() string {
 | |
| 	return fmt.Sprintf("Export(%v)", o.exported)
 | |
| }
 | |
| 
 | |
| func (o provideExportOption) applyProvideOption(opts *provideOptions) {
 | |
| 	opts.Exported = o.exported
 | |
| }
 | |
| 
 | |
| // provider encapsulates a user-provided constructor.
 | |
| type provider interface {
 | |
| 	// ID is a unique numerical identifier for this provider.
 | |
| 	ID() dot.CtorID
 | |
| 
 | |
| 	// Order reports the order of this provider in the graphHolder.
 | |
| 	// This value is usually returned by the graphHolder.NewNode method.
 | |
| 	Order(*Scope) int
 | |
| 
 | |
| 	// Location returns where this constructor was defined.
 | |
| 	Location() *digreflect.Func
 | |
| 
 | |
| 	// ParamList returns information about the direct dependencies of this
 | |
| 	// constructor.
 | |
| 	ParamList() paramList
 | |
| 
 | |
| 	// ResultList returns information about the values produced by this
 | |
| 	// constructor.
 | |
| 	ResultList() resultList
 | |
| 
 | |
| 	// Calls the underlying constructor, reading values from the
 | |
| 	// containerStore as needed.
 | |
| 	//
 | |
| 	// The values produced by this provider should be submitted into the
 | |
| 	// containerStore.
 | |
| 	Call(containerStore) error
 | |
| 
 | |
| 	CType() reflect.Type
 | |
| 
 | |
| 	OrigScope() *Scope
 | |
| }
 | |
| 
 | |
| // Provide teaches the container how to build values of one or more types and
 | |
| // expresses their dependencies.
 | |
| //
 | |
| // The first argument of Provide is a function that accepts zero or more
 | |
| // parameters and returns one or more results. The function may optionally
 | |
| // return an error to indicate that it failed to build the value. This
 | |
| // function will be treated as the constructor for all the types it returns.
 | |
| // This function will be called AT MOST ONCE when a type produced by it, or a
 | |
| // type that consumes this function's output, is requested via Invoke. If the
 | |
| // same types are requested multiple times, the previously produced value will
 | |
| // be reused.
 | |
| //
 | |
| // Provide accepts argument types or dig.In structs as dependencies, and
 | |
| // separate return values or dig.Out structs for results.
 | |
| func (c *Container) Provide(constructor interface{}, opts ...ProvideOption) error {
 | |
| 	return c.scope.Provide(constructor, opts...)
 | |
| }
 | |
| 
 | |
| // Provide teaches the Scope how to build values of one or more types and
 | |
| // expresses their dependencies.
 | |
| //
 | |
| // The first argument of Provide is a function that accepts zero or more
 | |
| // parameters and returns one or more results. The function may optionally
 | |
| // return an error to indicate that it failed to build the value. This
 | |
| // function will be treated as the constructor for all the types it returns.
 | |
| // This function will be called AT MOST ONCE when a type produced by it, or a
 | |
| // type that consumes this function's output, is requested via Invoke. If the
 | |
| // same types are requested multiple times, the previously produced value will
 | |
| // be reused.
 | |
| //
 | |
| // Provide accepts argument types or dig.In structs as dependencies, and
 | |
| // separate return values or dig.Out structs for results.
 | |
| //
 | |
| // When a constructor is Provided to a Scope, it will propagate this to any
 | |
| // Scopes that are descendents, but not ancestors of this Scope.
 | |
| // To provide a constructor to all the Scopes available, provide it to
 | |
| // Container, which is the root Scope.
 | |
| func (s *Scope) Provide(constructor interface{}, opts ...ProvideOption) error {
 | |
| 	ctype := reflect.TypeOf(constructor)
 | |
| 	if ctype == nil {
 | |
| 		return newErrInvalidInput("can't provide an untyped nil", nil)
 | |
| 	}
 | |
| 	if ctype.Kind() != reflect.Func {
 | |
| 		return newErrInvalidInput(
 | |
| 			fmt.Sprintf("must provide constructor function, got %v (type %v)", constructor, ctype), nil)
 | |
| 	}
 | |
| 
 | |
| 	var options provideOptions
 | |
| 	for _, o := range opts {
 | |
| 		o.applyProvideOption(&options)
 | |
| 	}
 | |
| 	if err := options.Validate(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err := s.provide(constructor, options); err != nil {
 | |
| 		var errFunc *digreflect.Func
 | |
| 		if options.Location == nil {
 | |
| 			errFunc = digreflect.InspectFunc(constructor)
 | |
| 		} else {
 | |
| 			errFunc = options.Location
 | |
| 		}
 | |
| 
 | |
| 		return errProvide{
 | |
| 			Func:   errFunc,
 | |
| 			Reason: err,
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (s *Scope) provide(ctor interface{}, opts provideOptions) (err error) {
 | |
| 	// If Export option is provided to the constructor, this should be injected to the
 | |
| 	// root-level Scope (Container) to allow it to propagate to all other Scopes.
 | |
| 	origScope := s
 | |
| 	if opts.Exported {
 | |
| 		s = s.rootScope()
 | |
| 	}
 | |
| 
 | |
| 	// For all scopes affected by this change,
 | |
| 	// take a snapshot of the current graph state before
 | |
| 	// we start making changes to it as we may need to
 | |
| 	// undo them upon encountering errors.
 | |
| 	allScopes := s.appendSubscopes(nil)
 | |
| 	for _, s := range allScopes {
 | |
| 		s := s
 | |
| 		s.gh.Snapshot()
 | |
| 		defer func() {
 | |
| 			if err != nil {
 | |
| 				s.gh.Rollback()
 | |
| 			}
 | |
| 		}()
 | |
| 	}
 | |
| 
 | |
| 	n, err := newConstructorNode(
 | |
| 		ctor,
 | |
| 		s,
 | |
| 		origScope,
 | |
| 		constructorOptions{
 | |
| 			ResultName:  opts.Name,
 | |
| 			ResultGroup: opts.Group,
 | |
| 			ResultAs:    opts.As,
 | |
| 			Location:    opts.Location,
 | |
| 			Callback:    opts.Callback,
 | |
| 		},
 | |
| 	)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	keys, err := s.findAndValidateResults(n.ResultList())
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	ctype := reflect.TypeOf(ctor)
 | |
| 	if len(keys) == 0 {
 | |
| 		return newErrInvalidInput(
 | |
| 			fmt.Sprintf("%v must provide at least one non-error type", ctype), nil)
 | |
| 	}
 | |
| 
 | |
| 	oldProviders := make(map[key][]*constructorNode)
 | |
| 	for k := range keys {
 | |
| 		// Cache old providers before running cycle detection.
 | |
| 		oldProviders[k] = s.providers[k]
 | |
| 		s.providers[k] = append(s.providers[k], n)
 | |
| 	}
 | |
| 
 | |
| 	for _, s := range allScopes {
 | |
| 		s.isVerifiedAcyclic = false
 | |
| 		if s.deferAcyclicVerification {
 | |
| 			continue
 | |
| 		}
 | |
| 		if ok, cycle := graph.IsAcyclic(s.gh); !ok {
 | |
| 			// When a cycle is detected, recover the old providers to reset
 | |
| 			// the providers map back to what it was before this node was
 | |
| 			// introduced.
 | |
| 			for k, ops := range oldProviders {
 | |
| 				s.providers[k] = ops
 | |
| 			}
 | |
| 
 | |
| 			return newErrInvalidInput("this function introduces a cycle", s.cycleDetectedError(cycle))
 | |
| 		}
 | |
| 		s.isVerifiedAcyclic = true
 | |
| 	}
 | |
| 
 | |
| 	s.nodes = append(s.nodes, n)
 | |
| 
 | |
| 	// Record introspection info for caller if Info option is specified
 | |
| 	if info := opts.Info; info != nil {
 | |
| 		params := n.ParamList().DotParam()
 | |
| 		results := n.ResultList().DotResult()
 | |
| 
 | |
| 		info.ID = (ID)(n.id)
 | |
| 		info.Inputs = make([]*Input, len(params))
 | |
| 		info.Outputs = make([]*Output, len(results))
 | |
| 
 | |
| 		for i, param := range params {
 | |
| 			info.Inputs[i] = &Input{
 | |
| 				t:        param.Type,
 | |
| 				optional: param.Optional,
 | |
| 				name:     param.Name,
 | |
| 				group:    param.Group,
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		for i, res := range results {
 | |
| 			info.Outputs[i] = &Output{
 | |
| 				t:     res.Type,
 | |
| 				name:  res.Name,
 | |
| 				group: res.Group,
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Builds a collection of all result types produced by this constructor.
 | |
| func (s *Scope) findAndValidateResults(rl resultList) (map[key]struct{}, error) {
 | |
| 	var err error
 | |
| 	keyPaths := make(map[key]string)
 | |
| 	walkResult(rl, connectionVisitor{
 | |
| 		s:        s,
 | |
| 		err:      &err,
 | |
| 		keyPaths: keyPaths,
 | |
| 	})
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	keys := make(map[key]struct{}, len(keyPaths))
 | |
| 	for k := range keyPaths {
 | |
| 		keys[k] = struct{}{}
 | |
| 	}
 | |
| 	return keys, nil
 | |
| }
 | |
| 
 | |
| // Visits the results of a node and compiles a collection of all the keys
 | |
| // produced by that node.
 | |
| type connectionVisitor struct {
 | |
| 	s *Scope
 | |
| 
 | |
| 	// If this points to a non-nil value, we've already encountered an error
 | |
| 	// and should stop traversing.
 | |
| 	err *error
 | |
| 
 | |
| 	// Map of keys provided to path that provided this. The path is a string
 | |
| 	// documenting which positional return value or dig.Out attribute is
 | |
| 	// providing this particular key.
 | |
| 	//
 | |
| 	// For example, "[0].Foo" indicates that the value was provided by the Foo
 | |
| 	// attribute of the dig.Out returned as the first result of the
 | |
| 	// constructor.
 | |
| 	keyPaths map[key]string
 | |
| 
 | |
| 	// We track the path to the current result here. For example, this will
 | |
| 	// be, ["[1]", "Foo", "Bar"] when we're visiting Bar in,
 | |
| 	//
 | |
| 	//   func() (io.Writer, struct {
 | |
| 	//     dig.Out
 | |
| 	//
 | |
| 	//     Foo struct {
 | |
| 	//       dig.Out
 | |
| 	//
 | |
| 	//       Bar io.Reader
 | |
| 	//     }
 | |
| 	//   })
 | |
| 	currentResultPath []string
 | |
| }
 | |
| 
 | |
| func (cv connectionVisitor) AnnotateWithField(f resultObjectField) resultVisitor {
 | |
| 	cv.currentResultPath = append(cv.currentResultPath, f.FieldName)
 | |
| 	return cv
 | |
| }
 | |
| 
 | |
| func (cv connectionVisitor) AnnotateWithPosition(i int) resultVisitor {
 | |
| 	cv.currentResultPath = append(cv.currentResultPath, fmt.Sprintf("[%d]", i))
 | |
| 	return cv
 | |
| }
 | |
| 
 | |
| func (cv connectionVisitor) Visit(res result) resultVisitor {
 | |
| 	// Already failed. Stop looking.
 | |
| 	if *cv.err != nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	path := strings.Join(cv.currentResultPath, ".")
 | |
| 
 | |
| 	switch r := res.(type) {
 | |
| 
 | |
| 	case resultSingle:
 | |
| 		k := key{name: r.Name, t: r.Type}
 | |
| 
 | |
| 		if err := cv.checkKey(k, path); err != nil {
 | |
| 			*cv.err = err
 | |
| 			return nil
 | |
| 		}
 | |
| 		for _, asType := range r.As {
 | |
| 			k := key{name: r.Name, t: asType}
 | |
| 			if err := cv.checkKey(k, path); err != nil {
 | |
| 				*cv.err = err
 | |
| 				return nil
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case resultGrouped:
 | |
| 		// we don't really care about the path for this since conflicts are
 | |
| 		// okay for group results. We'll track it for the sake of having a
 | |
| 		// value there.
 | |
| 		k := key{group: r.Group, t: r.Type}
 | |
| 		cv.keyPaths[k] = path
 | |
| 		for _, asType := range r.As {
 | |
| 			k := key{group: r.Group, t: asType}
 | |
| 			cv.keyPaths[k] = path
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return cv
 | |
| }
 | |
| 
 | |
| func (cv connectionVisitor) checkKey(k key, path string) error {
 | |
| 	defer func() { cv.keyPaths[k] = path }()
 | |
| 	if conflict, ok := cv.keyPaths[k]; ok {
 | |
| 		return newErrInvalidInput(fmt.Sprintf("cannot provide %v from %v", k, path),
 | |
| 			newErrInvalidInput(fmt.Sprintf("already provided by %v", conflict), nil))
 | |
| 	}
 | |
| 	if ps := cv.s.providers[k]; len(ps) > 0 {
 | |
| 		cons := make([]string, len(ps))
 | |
| 		for i, p := range ps {
 | |
| 			cons[i] = fmt.Sprint(p.Location())
 | |
| 		}
 | |
| 
 | |
| 		return newErrInvalidInput(fmt.Sprintf("cannot provide %v from %v", k, path),
 | |
| 			newErrInvalidInput(fmt.Sprintf("already provided by %v", strings.Join(cons, "; ")), nil))
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |