status-go/vendor/go.uber.org/dig/container.go

283 lines
8.5 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 (
"fmt"
"math/rand"
"reflect"
"go.uber.org/dig/internal/dot"
)
const (
_optionalTag = "optional"
_nameTag = "name"
_ignoreUnexportedTag = "ignore-unexported"
)
// Unique identification of an object in the graph.
type key struct {
t reflect.Type
// Only one of name or group will be set.
name string
group string
}
func (k key) String() string {
if k.name != "" {
return fmt.Sprintf("%v[name=%q]", k.t, k.name)
}
if k.group != "" {
return fmt.Sprintf("%v[group=%q]", k.t, k.group)
}
return k.t.String()
}
// Option configures a Container.
type Option interface {
applyOption(*Container)
}
// Container is a directed acyclic graph of types and their dependencies.
// A Container is the root Scope that represents the top-level scoped
// directed acyclic graph of the dependencies.
type Container struct {
// this is the "root" Scope that represents the
// root of the scope tree.
scope *Scope
}
// containerWriter provides write access to the Container's underlying data
// store.
type containerWriter interface {
// setValue sets the value with the given name and type in the container.
// If a value with the same name and type already exists, it will be
// overwritten.
setValue(name string, t reflect.Type, v reflect.Value)
// setDecoratedValue sets a decorated value with the given name and type
// in the container. If a decorated value with the same name and type already
// exists, it will be overwritten.
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)
// submitDecoratedGroupedValue submits a decorated value to the value group
// with the provided name.
submitDecoratedGroupedValue(name string, t reflect.Type, v reflect.Value)
}
// containerStore provides access to the Container's underlying data store.
type containerStore interface {
containerWriter
// Adds a new graph node to the Container
newGraphNode(w interface{}, orders map[*Scope]int)
// Returns a slice containing all known types.
knownTypes() []reflect.Type
// Retrieves the value with the provided name and type, if any.
getValue(name string, t reflect.Type) (v reflect.Value, ok bool)
// Retrieves a decorated value with the provided name and type, if any.
getDecoratedValue(name string, t reflect.Type) (v reflect.Value, ok bool)
// 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
// Retrieves all decorated values for the provided group and type, if any.
getDecoratedValueGroup(name string, t reflect.Type) (reflect.Value, bool)
// Returns the providers that can produce a value with the given name and
// type.
getValueProviders(name string, t reflect.Type) []provider
// Returns the providers that can produce values for the given group and
// type.
getGroupProviders(name string, t reflect.Type) []provider
// Returns the providers that can produce a value with the given name and
// type across all the Scopes that are in effect of this containerStore.
getAllValueProviders(name string, t reflect.Type) []provider
// Returns the decorator that can decorate values for the given name and
// type.
getValueDecorator(name string, t reflect.Type) (decorator, bool)
// Reutrns the decorator that can decorate values for the given group and
// type.
getGroupDecorator(name string, t reflect.Type) (decorator, bool)
// Reports a list of stores (starting at this store) up to the root
// store.
storesToRoot() []containerStore
createGraph() *dot.Graph
// Returns invokerFn function to use when calling arguments.
invoker() invokerFn
}
// New constructs a Container.
func New(opts ...Option) *Container {
s := newScope()
c := &Container{scope: s}
for _, opt := range opts {
opt.applyOption(c)
}
return c
}
// DeferAcyclicVerification is an Option to override the default behavior
// of container.Provide, deferring the dependency graph validation to no longer
// run after each call to container.Provide. The container will instead verify
// the graph on first `Invoke`.
//
// Applications adding providers to a container in a tight loop may experience
// performance improvements by initializing the container with this option.
func DeferAcyclicVerification() Option {
return deferAcyclicVerificationOption{}
}
type deferAcyclicVerificationOption struct{}
func (deferAcyclicVerificationOption) String() string {
return "DeferAcyclicVerification()"
}
func (deferAcyclicVerificationOption) applyOption(c *Container) {
c.scope.deferAcyclicVerification = true
}
// RecoverFromPanics is an [Option] to recover from panics that occur while
// running functions given to the container. When set, recovered panics
// will be placed into a [PanicError], and returned at the invoke callsite.
// See [PanicError] for an example on how to handle panics with this option
// enabled, and distinguish them from errors.
func RecoverFromPanics() Option {
return recoverFromPanicsOption{}
}
type recoverFromPanicsOption struct{}
func (recoverFromPanicsOption) String() string {
return "RecoverFromPanics()"
}
func (recoverFromPanicsOption) applyOption(c *Container) {
c.scope.recoverFromPanics = true
}
// Changes the source of randomness for the container.
//
// This will help provide determinism during tests.
func setRand(r *rand.Rand) Option {
return setRandOption{r: r}
}
type setRandOption struct{ r *rand.Rand }
func (o setRandOption) String() string {
return fmt.Sprintf("setRand(%p)", o.r)
}
func (o setRandOption) applyOption(c *Container) {
c.scope.rand = o.r
}
// DryRun is an Option which, when set to true, disables invocation of functions supplied to
// Provide and Invoke. Use this to build no-op containers.
func DryRun(dry bool) Option {
return dryRunOption(dry)
}
type dryRunOption bool
func (o dryRunOption) String() string {
return fmt.Sprintf("DryRun(%v)", bool(o))
}
func (o dryRunOption) applyOption(c *Container) {
if o {
c.scope.invokerFn = dryInvoker
} else {
c.scope.invokerFn = defaultInvoker
}
}
// invokerFn specifies how the container calls user-supplied functions.
type invokerFn func(fn reflect.Value, args []reflect.Value) (results []reflect.Value)
func defaultInvoker(fn reflect.Value, args []reflect.Value) []reflect.Value {
return fn.Call(args)
}
// Generates zero values for results without calling the supplied function.
func dryInvoker(fn reflect.Value, _ []reflect.Value) []reflect.Value {
ft := fn.Type()
results := make([]reflect.Value, ft.NumOut())
for i := 0; i < ft.NumOut(); i++ {
results[i] = reflect.Zero(fn.Type().Out(i))
}
return results
}
// String representation of the entire Container
func (c *Container) String() string {
return c.scope.String()
}
// Scope creates a child scope of the Container with the given name.
func (c *Container) Scope(name string, opts ...ScopeOption) *Scope {
return c.scope.Scope(name, opts...)
}
type byTypeName []reflect.Type
func (bs byTypeName) Len() int {
return len(bs)
}
func (bs byTypeName) Less(i int, j int) bool {
return fmt.Sprint(bs[i]) < fmt.Sprint(bs[j])
}
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))
for i, j := range rand.Perm(len(items)) {
newItems[i] = items[j]
}
return newItems
}