Integrate BACKBEAT SDK and resolve KACHING license validation

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>
This commit is contained in:
anthonyrawlins
2025-09-06 07:56:26 +10:00
parent 543ab216f9
commit 9bdcbe0447
4730 changed files with 1480093 additions and 1916 deletions

19
vendor/go.uber.org/dig/.codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,19 @@
coverage:
range: 70..98
round: down
precision: 2
status:
project: # measuring the overall project coverage
default: # context, you can create multiple ones with custom titles
enabled: yes # must be yes|true to enable this status
target: 97 # specify the target coverage for each commit status
# option: "auto" (must increase from parent commit or pull request base)
# option: "X%" a static target percentage to hit
if_not_found: success # if parent is not found report status as success, error, or failure
if_ci_failed: error # if ci fails report status as success, error, or failure
patch:
default:
enabled: yes
target: 70

12
vendor/go.uber.org/dig/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,12 @@
/bin
/vendor
/.bench
*.mem
*.cpu
*.test
*.log
*.out
*.html
*.coverprofile
coverage.txt
*.pprof

309
vendor/go.uber.org/dig/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,309 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.17.1] - 2023-10-19
### Added
- Suggestions for value vs. pointer elements for slice and array types.
### Fixed
- An issue where value group values were not getting decorated
by decorators within the same module when using dig.Export(true).
- A typo in docs.
- An issue where false positives in cycle detection were occurring
when providing to a child scope.
Thanks to @paullen and @lcarilla for their contributions to this release.
[1.17.1]: https://github.com/uber-go/dig/compare/v1.17.0...v1.17.1
## [1.17.0] - 2023-05-02
### Added
- Allow using `dig.As` with `dig.Group`.
- Add `FillInvokeInfo` Option and `InvokeInfo` struct to help
extract the types requested by an `Invoke` statement.
- To get visibility into constructor and decorator calls, introduce
`WithProviderCallback` and `WithDecoratorCallback` Options to provide callback functions.
[1.17.0]: https://github.com/uber-go/dig/compare/v1.16.1...v1.17.0
## [1.16.1] - 2023-01-10
### Fixed
- A panic when `DryRun` was used with `Decorate`.
[1.16.1]: https://github.com/uber-go/dig/compare/v1.16.0...v1.16.1
## [1.16.0] - 2023-01-03
### Added
- Add `RecoverFromPanics` option, which provides panic-recovery mechanism for Container.
- Add `Error` interface which enables distinguishing errors from Dig using standard `errors`
package.
Thanks to @mie998 for their contribution(s) to this release.
[1.16.0]: https://github.com/uber-go/dig/compare/v1.15.0...v1.16.0
## [1.15.0] - 2022-08-02
### Added
- Support for `soft` value groups, which specify a value group that only gets populated
with values from already-executed constructors.
### Fixed
- Fix an issue with invoke order affecting results provided by private provides
Thanks to @hbdf for their contributions to this release.
[1.15.0]: https://github.com/uber-go/dig/compare/v1.14.1...v1.15.0
## [1.14.1] - 2022-03-22
### Fixed
- Fix an issue where a dependency for a decoration supplied by another decorator in the
same scope is ignored.
- Fix a panic when submitting a single value as a value group in `Scope.Decorate`.
- Upon a provide error, make the error message contain the function named specified
by LocationForPC Option.
[1.14.1]: https://github.com/uber-go/dig/compare/v1.14.0...v1.14.1
## [1.14.0] - 2022-02-23
### Added
- Introduce `dig.Scope` which creates a scoped dependency injection
container to scope dependencies.
- Introduce `Scope.Decorate` and `Container.Decorate` which allows a
decorator to modify a dependency already provided in the dependency graph.
- Add `FillDecorateInfo` Option and `DecorateInfo` struct which exposes
information on what Dig was able to understand from the decorator provided
with `Scope.Decorate` or `Container.Decorate`.
### Changed
- The error message that appears when a cycle is detected in the dependency graph
has been changed slightly.
### Fixed
- A stack overflow bug that happens when cycles are introduced via self-pointing
dependencies with DeferAcyclicVerification.
[1.14.0]: https://github.com/uber-go/dig/compare/v1.13.0...v1.14.0
## [1.13.0] - 2021-09-21
### Added
- Introduce `As` option which supports providing a type as interface(s)
it implements to the container.
- Add `LocationForPC` option which overrides the function inspection
for a program counter address to a provided function info.
[1.13.0]: https://github.com/uber-go/dig/compare/v1.12.0...v1.13.0
## [1.12.0] - 2021-07-29
### Added
- Support for ProvideInfo and FillProvideInfo that allow the caller of
`Provide` to get info about what dig understood from the constructor.
[1.12.0]: https://github.com/uber-go/dig/compare/v1.11.0...v1.12.0
## [1.11.0] - 2021-06-09
### Added
- Support unexported fields on `dig.In` structs with the
`ignore-unexported:"true` struct tag.
[1.11.0]: https://github.com/uber-go/dig/compare/v1.10.0...v1.11.0
## [1.10.0] - 2020-06-16
### Added
- Introduce `DryRun` Option which, when set to true, disables invocation
of functions supplied to `Provide` and `Invoke`. This option will be
used to build no-op containers, for example for `fx.ValidateApp` method.
[1.10.0]: https://github.com/uber-go/dig/compare/v1.9.0...v1.10.0
## [1.9.0] - 2020-03-31
### Added
- GraphViz visualization of the graph now includes names of packages next to
constructors.
- Added a `flatten` modifier to group tags for slices to allow providing
individual elements instead of the slice for a group value. See package
doucmentation for more information.
### Changed
- Drop library dependency on `golang.org/x/lint`.
- Support printing multi-line error messages with `%+v`.
[1.9.0]: https://github.com/uber-go/dig/compare/v1.8.0...v1.9.0
## [1.8.0] - 2019-11-14
### Changed
- Migrated to Go modules.
[1.8.0]: https://github.com/uber-go/dig/compare/v1.7.0...v1.8.0
## [1.7.0] - 2019-01-04
### Added
- Added `Group` option for `Provide` to add value groups to the container without
rewriting constructors. See package doucmentation for more information.
[1.7.0]: https://github.com/uber-go/dig/compare/v1.6.0...v1.7.0
## [1.6.0] - 2018-11-06
### Changed
- When an error graph is visualized, the graph is pruned so that the graph only
contains failure nodes.
- Container visualization is now oriented from right to left.
[1.6.0]: https://github.com/uber-go/dig/compare/v1.5.1...v1.6.0
## [1.5.1] - 2018-11-01
### Fixed
- Fixed a test that was causing Dig to be unusable with Go Modules.
[1.5.1]: https://github.com/uber-go/dig/compare/v1.5.0...v1.5.1
## [1.5.0] - 2018-09-19
### Added
- Added a `DeferAcyclicVerification` container option that defers graph cycle
detection until the next Invoke.
### Changed
- Improved cycle-detection performance by 50x in certain degenerative cases.
[1.5.0]: https://github.com/uber-go/dig/compare/v1.4.0...v1.5.0
## [1.4.0] - 2018-08-16
### Added
- Added `Visualize` function to visualize the state of the container in the
GraphViz DOT format. This allows visualization of error types and the
dependency relationships of types in the container.
- Added `CanVisualizeError` function to determine if an error can be visualized
in the graph.
- Added `Name` option for `Provide` to add named values to the container
without rewriting constructors. See package documentation for more
information.
### Changed
- `name:"..."` tags on nested Result Objects will now cause errors instead of
being ignored.
[1.4.0]: https://github.com/uber-go/dig/compare/v1.3.0...v1.4.0
## [1.3.0] - 2017-12-04
### Changed
- Improved messages for errors thrown by Dig under a many scenarios to be more
informative.
[1.3.0]: https://github.com/uber-go/dig/compare/v1.2.0...v1.3.0
## [1.2.0] - 2017-11-07
### Added
- `dig.In` and `dig.Out` now support value groups, making it possible to
produce many values of the same type from different constructors. See package
documentation for more information.
[1.2.0]: https://github.com/uber-go/dig/compare/v1.1.0...v1.2.0
## [1.1.0] - 2017-09-15
### Added
- Added the `dig.RootCause` function which allows retrieving the original
constructor error that caused an `Invoke` failure.
### Changed
- Errors from `Invoke` now attempt to hint to the user a presence of a similar
type, for example a pointer to the requested type and vice versa.
[1.1.0]: https://github.com/uber-go/dig/compare/v1.0.0...v1.1.0
## [1.0.0] - 2017-07-31
First stable release: no breaking changes will be made in the 1.x series.
### Changed
- `Provide` and `Invoke` will now fail if `dig.In` or `dig.Out` structs
contain unexported fields. Previously these fields were ignored which often
led to confusion.
[1.0.0]: https://github.com/uber-go/dig/compare/v1.0.0-rc2...v1.0.0
## [1.0.0-rc2] - 2017-07-21
### Added
- Exported `dig.IsIn` and `dig.IsOut` so that consuming libraries can check if
a params or return struct embeds the `dig.In` and `dig.Out` types, respectively.
### Changed
- Added variadic options to all public APIS so that new functionality can be
introduced post v1.0.0 without introducing breaking changes.
- Functions with variadic arguments can now be passed to `dig.Provide` and
`dig.Invoke`. Previously this caused an error, whereas now the args will be ignored.
[1.0.0-rc2]: https://github.com/uber-go/dig/compare/v1.0.0-rc1...v1.0.0-rc2
## [1.0.0-rc1] - 2017-06-21
First release candidate.
[1.0.0-rc1]: https://github.com/uber-go/dig/compare/v0.5.0...v1.0.0-rc1
## [0.5.0] - 2017-06-19
### Added
- `dig.In` and `dig.Out` now support named instances, i.e.:
```go
type param struct {
dig.In
DB1 DB.Connection `name:"primary"`
DB2 DB.Connection `name:"secondary"`
}
```
### Fixed
- Structs compatible with `dig.In` and `dig.Out` may now be generated using
`reflect.StructOf`.
[0.5.0]: https://github.com/uber-go/dig/compare/v0.4.0...v0.5.0
## [0.4.0] - 2017-06-12
### Added
- Add `dig.In` embeddable type for advanced use-cases of specifying dependencies.
- Add `dig.Out` embeddable type for advanced use-cases of constructors
inserting types in the container.
- Add support for optional parameters through `optional:"true"` tag on `dig.In` objects.
- Add support for value types and many built-ins (maps, slices, channels).
### Changed
- **[Breaking]** Restrict the API surface to only `Provide` and `Invoke`.
- **[Breaking]** Update `Provide` method to accept variadic arguments.
### Removed
- **[Breaking]** Remove `Must*` funcs to greatly reduce API surface area.
- Providing constructors with common returned types results in an error.
[0.4.0]: https://github.com/uber-go/dig/compare/v0.3...v0.4.0
## [0.3] - 2017-05-02
### Added
- Add functionality to `Provide` to support constructor with `n` return
objects to be resolved into the `dig.Graph`
- Add `Invoke` function to invoke provided function and insert return
objects into the `dig.Graph`
### Changed
- Rename `RegisterAll` and `MustRegisterAll` to `ProvideAll` and
`MustProvideAll`.
[0.3]: https://github.com/uber-go/dig/compare/v0.2...v0.3
## [0.2] - 2017-03-27
### Changed
- Rename `Register` to `Provide` for clarity and to recude clash with other
Register functions.
- Rename `dig.Graph` to `dig.Container`.
### Removed
- Remove the package-level functions and the `DefaultGraph`.
[0.2]: https://github.com/uber-go/dig/compare/v0.1...v0.2
## 0.1 - 2017-03-23
Initial release.

19
vendor/go.uber.org/dig/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2017-2018 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.

67
vendor/go.uber.org/dig/Makefile generated vendored Normal file
View File

@@ -0,0 +1,67 @@
export GOBIN ?= $(shell pwd)/bin
GOLINT = $(GOBIN)/golint
STATICCHECK = $(GOBIN)/staticcheck
BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
GO_FILES = $(shell \
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
-o -name '*.go' -print | cut -b3-)
MODULES = . ./tools
.PHONY: all
all: build lint test
.PHONY: build
build:
go build ./...
.PHONY: install
install:
$(foreach dir,$(MODULES),( \
cd $(dir) && \
go mod download) && \
) true
.PHONY: lint
lint: $(GOLINT) $(STATICCHECK)
@rm -rf lint.log
@echo "Checking formatting..."
@gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log
@echo "Checking vet..."
@go vet ./... 2>&1 | tee -a lint.log
@echo "Checking lint..."
@$(GOLINT) ./... 2>&1 | tee -a lint.log
@echo "Checking staticcheck..."
@$(STATICCHECK) ./... 2>&1 | tee -a lint.log
@echo "Checking for unresolved FIXMEs..."
@git grep -i fixme | grep -v -e Makefile | tee -a lint.log
@echo "Checking for license headers..."
@./check_license.sh | tee -a lint.log
@[ ! -s lint.log ]
$(GOLINT): tools/go.mod
cd tools && go install golang.org/x/lint/golint
$(STATICCHECK): tools/go.mod
cd tools && go install honnef.co/go/tools/cmd/staticcheck
.PHONY: test
test:
go test -race ./...
.PHONY: cover
cover:
go test -race -coverprofile=cover.out -coverpkg=./... ./...
go tool cover -html=cover.out -o cover.html
.PHONY: bench
BENCH ?= .
bench:
go list ./... | xargs -n1 go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS)
.PHONY: tidy
tidy:
$(foreach dir,$(MODULES),(cd $(dir) && go mod tidy) &&) true

51
vendor/go.uber.org/dig/README.md generated vendored Normal file
View File

@@ -0,0 +1,51 @@
# :hammer_and_pick: dig [![GoDoc][doc-img]][doc] [![GitHub release][release-img]][release] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][report-card-img]][report-card]
A reflection based dependency injection toolkit for Go.
### Good for:
* Powering an application framework, e.g. [Fx](https://github.com/uber-go/fx).
* Resolving the object graph during process startup.
### Bad for:
* Using in place of an application framework, e.g. [Fx](https://github.com/uber-go/fx).
* Resolving dependencies after the process has already started.
* Exposing to user-land code as a [Service Locator](https://martinfowler.com/articles/injection.html#UsingAServiceLocator).
## Installation
We recommend consuming [SemVer](http://semver.org/) major version `1` using
your dependency manager of choice.
```
$ glide get 'go.uber.org/dig#^1'
$ dep ensure -add "go.uber.org/dig@v1"
$ go get 'go.uber.org/dig@v1'
```
## Stability
This library is `v1` and follows [SemVer](http://semver.org/) strictly.
No breaking changes will be made to exported APIs before `v2.0.0`.
[doc-img]: http://img.shields.io/badge/GoDoc-Reference-blue.svg
[doc]: https://godoc.org/go.uber.org/dig
[release-img]: https://img.shields.io/github/release/uber-go/dig.svg
[release]: https://github.com/uber-go/dig/releases
[ci-img]: https://github.com/uber-go/dig/actions/workflows/go.yml/badge.svg
[ci]: https://github.com/uber-go/dig/actions/workflows/go.yml
[cov-img]: https://codecov.io/gh/uber-go/dig/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/uber-go/dig/branch/master
[report-card-img]: https://goreportcard.com/badge/github.com/uber-go/dig
[report-card]: https://goreportcard.com/report/github.com/uber-go/dig
## Stargazers over time
[![Stargazers over time](https://starchart.cc/uber-go/dig.svg)](https://starchart.cc/uber-go/dig)

108
vendor/go.uber.org/dig/callback.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
// Copyright (c) 2023 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
// CallbackInfo contains information about a provided function or decorator
// called by Dig, and is passed to a [Callback] registered with
// [WithProviderCallback] or [WithDecoratorCallback].
type CallbackInfo struct {
// Name is the name of the function in the format:
// <package_name>.<function_name>
Name string
// Error contains the error returned by the [Callback]'s associated
// function, if any. When used in conjunction with [RecoverFromPanics],
// this will be set to a [PanicError] when the function panics.
Error error
}
// Callback is a function that can be registered with a provided function
// or decorator with [WithCallback] to cause it to be called after the
// provided function or decorator is run.
type Callback func(CallbackInfo)
// WithProviderCallback returns a [ProvideOption] which has Dig call
// the passed in [Callback] after the corresponding constructor finishes running.
//
// For example, the following prints a completion message
// after "myConstructor" finishes, including the error if any:
//
// c := dig.New()
// myCallback := func(ci CallbackInfo) {
// var errorAdd string
// if ci.Error != nil {
// errorAdd = fmt.Sprintf("with error: %v", ci.Error)
// }
// fmt.Printf("%q finished%v", ci.Name, errorAdd)
// }
// c.Provide(myConstructor, WithProviderCallback(myCallback)),
//
// Callbacks can also be specified for Decorators with [WithDecoratorCallback].
//
// See [CallbackInfo] for more info on the information passed to the [Callback].
func WithProviderCallback(callback Callback) ProvideOption {
return withCallbackOption{
callback: callback,
}
}
// WithDecoratorCallback returns a [DecorateOption] which has Dig call
// the passed in [Callback] after the corresponding decorator finishes running.
//
// For example, the following prints a completion message
// after "myDecorator" finishes, including the error if any:
//
// c := dig.New()
// myCallback := func(ci CallbackInfo) {
// var errorAdd string
// if ci.Error != nil {
// errorAdd = fmt.Sprintf("with error: %v", ci.Error)
// }
// fmt.Printf("%q finished%v", ci.Name, errorAdd)
// }
// c.Decorate(myDecorator, WithDecoratorCallback(myCallback)),
//
// Callbacks can also be specified for Constructors with [WithProviderCallback].
//
// See [CallbackInfo] for more info on the information passed to the [Callback].
func WithDecoratorCallback(callback Callback) DecorateOption {
return withCallbackOption{
callback: callback,
}
}
type withCallbackOption struct {
callback Callback
}
var (
_ ProvideOption = withCallbackOption{}
_ DecorateOption = withCallbackOption{}
)
func (o withCallbackOption) applyProvideOption(po *provideOptions) {
po.Callback = o.callback
}
func (o withCallbackOption) apply(do *decorateOptions) {
do.Callback = o.callback
}

17
vendor/go.uber.org/dig/check_license.sh generated vendored Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash -e
ERROR_COUNT=0
while read -r file
do
case "$(head -1 "${file}")" in
*"Copyright (c) "*" Uber Technologies, Inc.")
# everything's cool
;;
*)
echo "$file is missing license header."
(( ERROR_COUNT++ ))
;;
esac
done < <(git ls-files "*\.go")
exit $ERROR_COUNT

243
vendor/go.uber.org/dig/constructor.go generated vendored Normal file
View File

@@ -0,0 +1,243 @@
// 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"
"reflect"
"go.uber.org/dig/internal/digerror"
"go.uber.org/dig/internal/digreflect"
"go.uber.org/dig/internal/dot"
)
// constructorNode is a node in the dependency graph that represents
// a constructor provided by the user.
//
// constructorNodes can produce zero or more values that they store into the container.
// For the Provide path, we verify that constructorNodes produce at least one value,
// otherwise the function will never be called.
type constructorNode struct {
ctor interface{}
ctype reflect.Type
// Location where this function was defined.
location *digreflect.Func
// id uniquely identifies the constructor that produces a node.
id dot.CtorID
// Whether the constructor owned by this node was already called.
called bool
// Type information about constructor parameters.
paramList paramList
// Type information about constructor results.
resultList resultList
// Order of this node in each Scopes' graphHolders.
orders map[*Scope]int
// Scope this node is part of.
s *Scope
// Scope this node was originally provided to.
// This is different from s if and only if the constructor was Provided with ExportOption.
origS *Scope
// Callback for this provided function, if there is one.
callback Callback
}
type constructorOptions struct {
// If specified, all values produced by this constructor have the provided name
// belong to the specified value group or implement any of the interfaces.
ResultName string
ResultGroup string
ResultAs []interface{}
Location *digreflect.Func
Callback Callback
}
func newConstructorNode(ctor interface{}, s *Scope, origS *Scope, opts constructorOptions) (*constructorNode, error) {
cval := reflect.ValueOf(ctor)
ctype := cval.Type()
cptr := cval.Pointer()
params, err := newParamList(ctype, s)
if err != nil {
return nil, err
}
results, err := newResultList(
ctype,
resultOptions{
Name: opts.ResultName,
Group: opts.ResultGroup,
As: opts.ResultAs,
},
)
if err != nil {
return nil, err
}
location := opts.Location
if location == nil {
location = digreflect.InspectFunc(ctor)
}
n := &constructorNode{
ctor: ctor,
ctype: ctype,
location: location,
id: dot.CtorID(cptr),
paramList: params,
resultList: results,
orders: make(map[*Scope]int),
s: s,
origS: origS,
callback: opts.Callback,
}
s.newGraphNode(n, n.orders)
return n, nil
}
func (n *constructorNode) Location() *digreflect.Func { return n.location }
func (n *constructorNode) ParamList() paramList { return n.paramList }
func (n *constructorNode) ResultList() resultList { return n.resultList }
func (n *constructorNode) ID() dot.CtorID { return n.id }
func (n *constructorNode) CType() reflect.Type { return n.ctype }
func (n *constructorNode) Order(s *Scope) int { return n.orders[s] }
func (n *constructorNode) OrigScope() *Scope { return n.origS }
// CopyOrder copies the order for the given parent scope to the given child scope.
func (n *constructorNode) CopyOrder(parent, child *Scope) {
n.orders[child] = n.orders[parent]
}
func (n *constructorNode) String() string {
return fmt.Sprintf("deps: %v, ctor: %v", n.paramList, n.ctype)
}
// Call calls this constructor if it hasn't already been called and
// injects any values produced by it into the provided container.
func (n *constructorNode) Call(c containerStore) (err error) {
if n.called {
return nil
}
if err := shallowCheckDependencies(c, n.paramList); err != nil {
return errMissingDependencies{
Func: n.location,
Reason: err,
}
}
args, err := n.paramList.BuildList(c)
if err != nil {
return errArgumentsFailed{
Func: n.location,
Reason: err,
}
}
if n.callback != nil {
// Wrap in separate func to include PanicErrors
defer func() {
n.callback(CallbackInfo{
Name: fmt.Sprintf("%v.%v", n.location.Package, n.location.Name),
Error: err,
})
}()
}
if n.s.recoverFromPanics {
defer func() {
if p := recover(); p != nil {
err = PanicError{
fn: n.location,
Panic: p,
}
}
}()
}
receiver := newStagingContainerWriter()
results := c.invoker()(reflect.ValueOf(n.ctor), args)
if err = n.resultList.ExtractList(receiver, false /* decorating */, results); err != nil {
return errConstructorFailed{Func: n.location, Reason: err}
}
// Commit the result to the original container that this constructor
// was supplied to. The provided constructor is only used for a view of
// the rest of the graph to instantiate the dependencies of this
// container.
receiver.Commit(n.s)
n.called = true
return nil
}
// stagingContainerWriter is a containerWriter that records the changes that
// 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
}
var _ containerWriter = (*stagingContainerWriter)(nil)
func newStagingContainerWriter() *stagingContainerWriter {
return &stagingContainerWriter{
values: make(map[key]reflect.Value),
groups: make(map[key][]reflect.Value),
}
}
func (sr *stagingContainerWriter) setValue(name string, t reflect.Type, v reflect.Value) {
sr.values[key{t: t, name: name}] = v
}
func (sr *stagingContainerWriter) setDecoratedValue(_ string, _ reflect.Type, _ reflect.Value) {
digerror.BugPanicf("stagingContainerWriter.setDecoratedValue must never be called")
}
func (sr *stagingContainerWriter) submitGroupedValue(group string, t reflect.Type, v reflect.Value) {
k := key{t: t, group: group}
sr.groups[k] = append(sr.groups[k], v)
}
func (sr *stagingContainerWriter) submitDecoratedGroupedValue(_ string, _ reflect.Type, _ reflect.Value) {
digerror.BugPanicf("stagingContainerWriter.submitDecoratedGroupedValue must never be called")
}
// Commit commits the received results to the provided containerWriter.
func (sr *stagingContainerWriter) Commit(cw containerWriter) {
for k, v := range sr.values {
cw.setValue(k.name, k.t, v)
}
for k, vs := range sr.groups {
for _, v := range vs {
cw.submitGroupedValue(k.group, k.t, v)
}
}
}

282
vendor/go.uber.org/dig/container.go generated vendored Normal file
View File

@@ -0,0 +1,282 @@
// 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
}

79
vendor/go.uber.org/dig/cycle_error.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
// Copyright (c) 2019 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"
"errors"
"fmt"
"io"
"go.uber.org/dig/internal/digreflect"
)
type cycleErrPathEntry struct {
Key key
Func *digreflect.Func
}
type errCycleDetected struct {
Path []cycleErrPathEntry
scope *Scope
}
var _ digError = errCycleDetected{}
func (e errCycleDetected) Error() string {
// We get something like,
//
// [scope "foo"]
// func(*bar) *foo provided by "path/to/package".NewFoo (path/to/file.go:42)
// depends on func(*baz) *bar provided by "another/package".NewBar (somefile.go:1)
// depends on func(*foo) baz provided by "somepackage".NewBar (anotherfile.go:2)
// depends on func(*bar) *foo provided by "path/to/package".NewFoo (path/to/file.go:42)
//
b := new(bytes.Buffer)
if name := e.scope.name; len(name) > 0 {
fmt.Fprintf(b, "[scope %q]\n", name)
}
for i, entry := range e.Path {
if i > 0 {
b.WriteString("\n\tdepends on ")
}
fmt.Fprintf(b, "%v provided by %v", entry.Key, entry.Func)
}
return b.String()
}
func (e errCycleDetected) writeMessage(w io.Writer, v string) {
fmt.Fprint(w, e.Error())
}
func (e errCycleDetected) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
// IsCycleDetected returns a boolean as to whether the provided error indicates
// a cycle was detected in the container graph.
func IsCycleDetected(err error) bool {
return errors.As(err, &errCycleDetected{})
}

313
vendor/go.uber.org/dig/decorate.go generated vendored Normal file
View File

@@ -0,0 +1,313 @@
// Copyright (c) 2022 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"
"reflect"
"go.uber.org/dig/internal/digreflect"
"go.uber.org/dig/internal/dot"
)
type decoratorState int
const (
decoratorReady decoratorState = iota
decoratorOnStack
decoratorCalled
)
type decorator interface {
Call(c containerStore) error
ID() dot.CtorID
State() decoratorState
}
type decoratorNode struct {
dcor interface{}
dtype reflect.Type
id dot.CtorID
// Location where this function was defined.
location *digreflect.Func
// Current state of this decorator
state decoratorState
// Parameters of the decorator.
params paramList
// Results of the decorator.
results resultList
// Order of this node in each Scopes' graphHolders.
orders map[*Scope]int
// Scope this node was originally provided to.
s *Scope
// Callback for this decorator, if there is one.
callback Callback
}
func newDecoratorNode(dcor interface{}, s *Scope, opts decorateOptions) (*decoratorNode, error) {
dval := reflect.ValueOf(dcor)
dtype := dval.Type()
dptr := dval.Pointer()
pl, err := newParamList(dtype, s)
if err != nil {
return nil, err
}
rl, err := newResultList(dtype, resultOptions{})
if err != nil {
return nil, err
}
n := &decoratorNode{
dcor: dcor,
dtype: dtype,
id: dot.CtorID(dptr),
location: digreflect.InspectFunc(dcor),
orders: make(map[*Scope]int),
params: pl,
results: rl,
s: s,
callback: opts.Callback,
}
return n, nil
}
func (n *decoratorNode) Call(s containerStore) (err error) {
if n.state == decoratorCalled {
return nil
}
n.state = decoratorOnStack
if err := shallowCheckDependencies(s, n.params); err != nil {
return errMissingDependencies{
Func: n.location,
Reason: err,
}
}
args, err := n.params.BuildList(n.s)
if err != nil {
return errArgumentsFailed{
Func: n.location,
Reason: err,
}
}
if n.callback != nil {
// Wrap in separate func to include PanicErrors
defer func() {
n.callback(CallbackInfo{
Name: fmt.Sprintf("%v.%v", n.location.Package, n.location.Name),
Error: err,
})
}()
}
if n.s.recoverFromPanics {
defer func() {
if p := recover(); p != nil {
err = PanicError{
fn: n.location,
Panic: p,
}
}
}()
}
results := s.invoker()(reflect.ValueOf(n.dcor), args)
if err = n.results.ExtractList(n.s, true /* decorated */, results); err != nil {
return err
}
n.state = decoratorCalled
return nil
}
func (n *decoratorNode) ID() dot.CtorID { return n.id }
func (n *decoratorNode) State() decoratorState { return n.state }
// DecorateOption modifies the default behavior of Decorate.
type DecorateOption interface {
apply(*decorateOptions)
}
type decorateOptions struct {
Info *DecorateInfo
Callback Callback
}
// FillDecorateInfo is a DecorateOption that writes info on what Dig was
// able to get out of the provided decorator into the provided DecorateInfo.
func FillDecorateInfo(info *DecorateInfo) DecorateOption {
return fillDecorateInfoOption{info: info}
}
type fillDecorateInfoOption struct{ info *DecorateInfo }
func (o fillDecorateInfoOption) String() string {
return fmt.Sprintf("FillDecorateInfo(%p)", o.info)
}
func (o fillDecorateInfoOption) apply(opts *decorateOptions) {
opts.Info = o.info
}
// DecorateInfo provides information about the decorator's inputs and outputs
// types as strings, as well as the ID of the decorator supplied to the Container.
type DecorateInfo struct {
ID ID
Inputs []*Input
Outputs []*Output
}
// Decorate provides a decorator for a type that has already been provided in the Container.
// Decorations at this level affect all scopes of the container.
// See Scope.Decorate for information on how to use this method.
func (c *Container) Decorate(decorator interface{}, opts ...DecorateOption) error {
return c.scope.Decorate(decorator, opts...)
}
// Decorate provides a decorator for a type that has already been provided in the Scope.
//
// Similar to Provide, Decorate takes in a function with zero or more dependencies and one
// or more results. Decorate can be used to modify a type that was already introduced to the
// Scope, or completely replace it with a new object.
//
// For example,
//
// s.Decorate(func(log *zap.Logger) *zap.Logger {
// return log.Named("myapp")
// })
//
// This takes in a value, augments it with a name, and returns a replacement for it. Functions
// in the Scope's dependency graph that use *zap.Logger will now use the *zap.Logger
// returned by this decorator.
//
// A decorator can also take in multiple parameters and replace one of them:
//
// s.Decorate(func(log *zap.Logger, cfg *Config) *zap.Logger {
// return log.Named(cfg.Name)
// })
//
// Or replace a subset of them:
//
// s.Decorate(func(
// log *zap.Logger,
// cfg *Config,
// scope metrics.Scope
// ) (*zap.Logger, metrics.Scope) {
// log = log.Named(cfg.Name)
// scope = scope.With(metrics.Tag("service", cfg.Name))
// return log, scope
// })
//
// Decorating a Scope affects all the child scopes of this Scope.
//
// Similar to a provider, the decorator function gets called *at most once*.
func (s *Scope) Decorate(decorator interface{}, opts ...DecorateOption) error {
var options decorateOptions
for _, opt := range opts {
opt.apply(&options)
}
dn, err := newDecoratorNode(decorator, s, options)
if err != nil {
return err
}
keys, err := findResultKeys(dn.results)
if err != nil {
return err
}
for _, k := range keys {
if _, ok := s.decorators[k]; ok {
return newErrInvalidInput(
fmt.Sprintf("cannot decorate using function %v: %s already decorated", dn.dtype, k), nil)
}
s.decorators[k] = dn
}
if info := options.Info; info != nil {
params := dn.params.DotParam()
results := dn.results.DotResult()
info.ID = (ID)(dn.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
}
func findResultKeys(r resultList) ([]key, error) {
// use BFS to search for all keys included in a resultList.
var (
q []result
keys []key
)
q = append(q, r)
for len(q) > 0 {
res := q[0]
q = q[1:]
switch innerResult := res.(type) {
case resultSingle:
keys = append(keys, key{t: innerResult.Type, name: innerResult.Name})
case resultGrouped:
if innerResult.Type.Kind() != reflect.Slice {
return nil, newErrInvalidInput("decorating a value group requires decorating the entire value group, not a single value", nil)
}
keys = append(keys, key{t: innerResult.Type.Elem(), group: innerResult.Group})
case resultObject:
for _, f := range innerResult.Fields {
q = append(q, f.Result)
}
case resultList:
q = append(q, innerResult.Results...)
}
}
return keys, nil
}

348
vendor/go.uber.org/dig/doc.go generated vendored Normal file
View File

@@ -0,0 +1,348 @@
// Copyright (c) 2019 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 provides an opinionated way of resolving object dependencies.
//
// # Status
//
// STABLE. No breaking changes will be made in this major version.
//
// # Container
//
// Dig exposes type Container as an object capable of resolving a directed
// acyclic dependency graph. Use the New function to create one.
//
// c := dig.New()
//
// # Provide
//
// Constructors for different types are added to the container by using the
// Provide method. A constructor can declare a dependency on another type by
// simply adding it as a function parameter. Dependencies for a type can be
// added to the graph both, before and after the type was added.
//
// err := c.Provide(func(conn *sql.DB) (*UserGateway, error) {
// // ...
// })
// if err != nil {
// // ...
// }
//
// if err := c.Provide(newDBConnection); err != nil {
// // ...
// }
//
// Multiple constructors can rely on the same type. The container creates a
// singleton for each retained type, instantiating it at most once when
// requested directly or as a dependency of another type.
//
// err := c.Provide(func(conn *sql.DB) *CommentGateway {
// // ...
// })
// if err != nil {
// // ...
// }
//
// Constructors can declare any number of dependencies as parameters and
// optionally, return errors.
//
// err := c.Provide(func(u *UserGateway, c *CommentGateway) (*RequestHandler, error) {
// // ...
// })
// if err != nil {
// // ...
// }
//
// if err := c.Provide(newHTTPServer); err != nil {
// // ...
// }
//
// Constructors can also return multiple results to add multiple types to the
// container.
//
// err := c.Provide(func(conn *sql.DB) (*UserGateway, *CommentGateway, error) {
// // ...
// })
// if err != nil {
// // ...
// }
//
// Constructors that accept a variadic number of arguments are treated as if
// they don't have those arguments. That is,
//
// func NewVoteGateway(db *sql.DB, options ...Option) *VoteGateway
//
// Is treated the same as,
//
// func NewVoteGateway(db *sql.DB) *VoteGateway
//
// The constructor will be called with all other dependencies and no variadic
// arguments.
//
// # Invoke
//
// Types added to the container may be consumed by using the Invoke method.
// Invoke accepts any function that accepts one or more parameters and
// optionally, returns an error. Dig calls the function with the requested
// type, instantiating only those types that were requested by the function.
// The call fails if any type or its dependencies (both direct and transitive)
// were not available in the container.
//
// err := c.Invoke(func(l *log.Logger) {
// // ...
// })
// if err != nil {
// // ...
// }
//
// err := c.Invoke(func(server *http.Server) error {
// // ...
// })
// if err != nil {
// // ...
// }
//
// Any error returned by the invoked function is propagated back to the
// caller.
//
// # Parameter Objects
//
// Constructors declare their dependencies as function parameters. This can
// very quickly become unreadable if the constructor has a lot of
// dependencies.
//
// func NewHandler(users *UserGateway, comments *CommentGateway, posts *PostGateway, votes *VoteGateway, authz *AuthZGateway) *Handler {
// // ...
// }
//
// A pattern employed to improve readability in a situation like this is to
// create a struct that lists all the parameters of the function as fields and
// changing the function to accept that struct instead. This is referred to as
// a parameter object.
//
// Dig has first class support for parameter objects: any struct embedding
// dig.In gets treated as a parameter object. The following is equivalent to
// the constructor above.
//
// type HandlerParams struct {
// dig.In
//
// Users *UserGateway
// Comments *CommentGateway
// Posts *PostGateway
// Votes *VoteGateway
// AuthZ *AuthZGateway
// }
//
// func NewHandler(p HandlerParams) *Handler {
// // ...
// }
//
// Handlers can receive any combination of parameter objects and parameters.
//
// func NewHandler(p HandlerParams, l *log.Logger) *Handler {
// // ...
// }
//
// # Result Objects
//
// Result objects are the flip side of parameter objects. These are structs
// that represent multiple outputs from a single function as fields in the
// struct. Structs embedding dig.Out get treated as result objects.
//
// func SetupGateways(conn *sql.DB) (*UserGateway, *CommentGateway, *PostGateway, error) {
// // ...
// }
//
// The above is equivalent to,
//
// type Gateways struct {
// dig.Out
//
// Users *UserGateway
// Comments *CommentGateway
// Posts *PostGateway
// }
//
// func SetupGateways(conn *sql.DB) (Gateways, error) {
// // ...
// }
//
// # Optional Dependencies
//
// Constructors often don't have a hard dependency on some types and
// are able to operate in a degraded state when that dependency is missing.
// Dig supports declaring dependencies as optional by adding an
// `optional:"true"` tag to fields of a dig.In struct.
//
// Fields in a dig.In structs that have the `optional:"true"` tag are treated
// as optional by Dig.
//
// type UserGatewayParams struct {
// dig.In
//
// Conn *sql.DB
// Cache *redis.Client `optional:"true"`
// }
//
// If an optional field is not available in the container, the constructor
// will receive a zero value for the field.
//
// func NewUserGateway(p UserGatewayParams, log *log.Logger) (*UserGateway, error) {
// if p.Cache == nil {
// log.Print("Caching disabled")
// }
// // ...
// }
//
// Constructors that declare dependencies as optional MUST handle the case of
// those dependencies being absent.
//
// The optional tag also allows adding new dependencies without breaking
// existing consumers of the constructor.
//
// # Named Values
//
// Some use cases call for multiple values of the same type. Dig allows adding
// multiple values of the same type to the container with the use of Named
// Values.
//
// Named Values can be produced by passing the dig.Name option when a
// constructor is provided. All values produced by that constructor will have
// the given name.
//
// Given the following constructors,
//
// func NewReadOnlyConnection(...) (*sql.DB, error)
// func NewReadWriteConnection(...) (*sql.DB, error)
//
// You can provide *sql.DB into a Container under different names by passing
// the dig.Name option.
//
// c.Provide(NewReadOnlyConnection, dig.Name("ro"))
// c.Provide(NewReadWriteConnection, dig.Name("rw"))
//
// Alternatively, you can produce a dig.Out struct and tag its fields with
// `name:".."` to have the corresponding value added to the graph under the
// specified name.
//
// type ConnectionResult struct {
// dig.Out
//
// ReadWrite *sql.DB `name:"rw"`
// ReadOnly *sql.DB `name:"ro"`
// }
//
// func ConnectToDatabase(...) (ConnectionResult, error) {
// // ...
// return ConnectionResult{ReadWrite: rw, ReadOnly: ro}, nil
// }
//
// Regardless of how a Named Value was produced, it can be consumed by another
// constructor by accepting a dig.In struct which has exported fields with the
// same name AND type that you provided.
//
// type GatewayParams struct {
// dig.In
//
// WriteToConn *sql.DB `name:"rw"`
// ReadFromConn *sql.DB `name:"ro"`
// }
//
// The name tag may be combined with the optional tag to declare the
// dependency optional.
//
// type GatewayParams struct {
// dig.In
//
// WriteToConn *sql.DB `name:"rw"`
// ReadFromConn *sql.DB `name:"ro" optional:"true"`
// }
//
// func NewCommentGateway(p GatewayParams, log *log.Logger) (*CommentGateway, error) {
// if p.ReadFromConn == nil {
// log.Print("Warning: Using RW connection for reads")
// p.ReadFromConn = p.WriteToConn
// }
// // ...
// }
//
// # Value Groups
//
// Added in Dig 1.2.
//
// Dig provides value groups to allow producing and consuming many values of
// the same type. Value groups allow constructors to send values to a named,
// unordered collection in the container. Other constructors can request all
// values in this collection as a slice.
//
// Constructors can send values into value groups by returning a dig.Out
// struct tagged with `group:".."`.
//
// type HandlerResult struct {
// dig.Out
//
// Handler Handler `group:"server"`
// }
//
// func NewHelloHandler() HandlerResult {
// ..
// }
//
// func NewEchoHandler() HandlerResult {
// ..
// }
//
// Any number of constructors may provide values to this named collection.
// Other constructors can request all values for this collection by requesting
// a slice tagged with `group:".."`. This will execute all constructors that
// provide a value to that group in an unspecified order.
//
// type ServerParams struct {
// dig.In
//
// Handlers []Handler `group:"server"`
// }
//
// func NewServer(p ServerParams) *Server {
// server := newServer()
// for _, h := range p.Handlers {
// server.Register(h)
// }
// return server
// }
//
// Note that values in a value group are unordered. Dig makes no guarantees
// about the order in which these values will be produced.
//
// Value groups can be used to provide multiple values for a group from a
// dig.Out using slices, however considering groups are retrieved by requesting
// a slice this implies that the values must be retrieved using a slice of
// slices. As of dig v1.9.0, if you want to provide individual elements to the
// group instead of the slice itself, you can add the `flatten` modifier to the
// group from a dig.Out.
//
// type IntResult struct {
// dig.Out
//
// Handler []int `group:"server"` // [][]int from dig.In
// Handler []int `group:"server,flatten"` // []int from dig.In
// }
package dig // import "go.uber.org/dig"

527
vendor/go.uber.org/dig/error.go generated vendored Normal file
View File

@@ -0,0 +1,527 @@
// Copyright (c) 2019 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 (
"errors"
"fmt"
"io"
"reflect"
"sort"
"go.uber.org/dig/internal/digreflect"
"go.uber.org/dig/internal/dot"
)
// Error is an interface implemented by all Dig errors.
//
// Use this interface, in conjunction with [RootCause], in order to
// determine if errors you encounter come from Dig, or if they come
// from provided constructors or invoked functions. See [RootCause]
// for more info.
type Error interface {
error
// Writes the message or context for this error in the chain.
//
// Note: the Error interface must always have a private function
// such as this one in order to maintain properly sealed.
//
// verb is either %v or %+v.
writeMessage(w io.Writer, v string)
}
// a digError is a dig.Error with additional functionality for
// internal use - namely the ability to be formatted.
type digError interface {
Error
fmt.Formatter
}
// A PanicError occurs when a panic occurs while running functions given to the container
// with the [RecoverFromPanic] option being set. It contains the panic message from the
// original panic. A PanicError does not wrap other errors, and it does not implement
// dig.Error, meaning it will be returned from [RootCause]. With the [RecoverFromPanic]
// option set, a panic can be distinguished from dig errors and errors from provided/
// invoked/decorated functions like so:
//
// rootCause := dig.RootCause(err)
//
// var pe dig.PanicError
// var de dig.Error
// if errors.As(rootCause, &pe) {
// // This is caused by a panic
// } else if errors.As(err, &de) {
// // This is a dig error
// } else {
// // This is an error from one of my provided/invoked functions or decorators
// }
//
// Or, if only interested in distinguishing panics from errors:
//
// var pe dig.PanicError
// if errors.As(err, &pe) {
// // This is caused by a panic
// } else {
// // This is an error
// }
type PanicError struct {
// The function the panic occurred at
fn *digreflect.Func
// The panic that was returned from recover()
Panic any
}
// Format will format the PanicError, expanding the corresponding function if in +v mode.
func (e PanicError) Format(w fmt.State, c rune) {
if w.Flag('+') && c == 'v' {
fmt.Fprintf(w, "panic: %q in func: %+v", e.Panic, e.fn)
} else {
fmt.Fprintf(w, "panic: %q in func: %v", e.Panic, e.fn)
}
}
func (e PanicError) Error() string {
return fmt.Sprint(e)
}
// formatError will call a dig.Error's writeMessage() method to print the error message
// and then will automatically attempt to print errors wrapped underneath (which can create
// a recursive effect if the wrapped error's Format() method then points back to this function).
func formatError(e digError, w fmt.State, v rune) {
multiline := w.Flag('+') && v == 'v'
verb := "%v"
if multiline {
verb = "%+v"
}
// "context: " or "context:\n"
e.writeMessage(w, verb)
// Will route back to this function recursively if next error
// is also wrapped and points back here
wrappedError := errors.Unwrap(e)
if wrappedError == nil {
return
}
io.WriteString(w, ":")
if multiline {
io.WriteString(w, "\n")
} else {
io.WriteString(w, " ")
}
fmt.Fprintf(w, verb, wrappedError)
}
// RootCause returns the first non-dig.Error in a chain of wrapped
// errors, if there is one. Otherwise, RootCause returns the error
// on the bottom of the chain of wrapped errors.
//
// Use this function and errors.As to differentiate between Dig errors
// and errors thrown by provided constructors or invoked functions:
//
// rootCause := dig.RootCause(err)
// var de dig.Error
// if errors.As(rootCause, &de) {
// // Is a Dig error
// } else {
// // Is an error thrown by one of my provided/invoked/decorated functions
// }
//
// See [PanicError] for an example showing how to additionally detect
// and handle panics in provided/invoked/decorated functions.
func RootCause(err error) error {
var de Error
// Dig down to first non dig.Error, or bottom of chain
for ; errors.As(err, &de); err = errors.Unwrap(de) {
}
if err == nil {
return de
}
return err
}
// errInvalidInput is returned whenever the user provides bad input when
// interacting with the container. May optionally have a more detailed
// error wrapped underneath.
type errInvalidInput struct {
Message string
Cause error
}
var _ digError = errInvalidInput{}
// newErrInvalidInput creates a new errInvalidInput, wrapping the given
// other error that caused this error. If there is no underlying cause,
// pass in nil. This will cause all attempts to unwrap this error to return
// nil, replicating errors.Unwrap's behavior when passed an error without
// an Unwrap() method.
func newErrInvalidInput(msg string, cause error) errInvalidInput {
return errInvalidInput{msg, cause}
}
func (e errInvalidInput) Error() string { return fmt.Sprint(e) }
func (e errInvalidInput) Unwrap() error { return e.Cause }
func (e errInvalidInput) writeMessage(w io.Writer, _ string) {
fmt.Fprintf(w, e.Message)
}
func (e errInvalidInput) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
// errProvide is returned when a constructor could not be Provided into the
// container.
type errProvide struct {
Func *digreflect.Func
Reason error
}
var _ digError = errProvide{}
func (e errProvide) Error() string { return fmt.Sprint(e) }
func (e errProvide) Unwrap() error { return e.Reason }
func (e errProvide) writeMessage(w io.Writer, verb string) {
fmt.Fprintf(w, "cannot provide function "+verb, e.Func)
}
func (e errProvide) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
// errConstructorFailed is returned when a user-provided constructor failed
// with a non-nil error.
type errConstructorFailed struct {
Func *digreflect.Func
Reason error
}
var _ digError = errConstructorFailed{}
func (e errConstructorFailed) Error() string { return fmt.Sprint(e) }
func (e errConstructorFailed) Unwrap() error { return e.Reason }
func (e errConstructorFailed) writeMessage(w io.Writer, verb string) {
fmt.Fprintf(w, "received non-nil error from function "+verb, e.Func)
}
func (e errConstructorFailed) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
// errArgumentsFailed is returned when a function could not be run because one
// of its dependencies failed to build for any reason.
type errArgumentsFailed struct {
Func *digreflect.Func
Reason error
}
var _ digError = errArgumentsFailed{}
func (e errArgumentsFailed) Error() string { return fmt.Sprint(e) }
func (e errArgumentsFailed) Unwrap() error { return e.Reason }
func (e errArgumentsFailed) writeMessage(w io.Writer, verb string) {
fmt.Fprintf(w, "could not build arguments for function "+verb, e.Func)
}
func (e errArgumentsFailed) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
// errMissingDependencies is returned when the dependencies of a function are
// not available in the container.
type errMissingDependencies struct {
Func *digreflect.Func
Reason error
}
var _ digError = errMissingDependencies{}
func (e errMissingDependencies) Error() string { return fmt.Sprint(e) }
func (e errMissingDependencies) Unwrap() error { return e.Reason }
func (e errMissingDependencies) writeMessage(w io.Writer, verb string) {
fmt.Fprintf(w, "missing dependencies for function "+verb, e.Func)
}
func (e errMissingDependencies) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
// errParamSingleFailed is returned when a paramSingle could not be built.
type errParamSingleFailed struct {
Key key
Reason error
CtorID dot.CtorID
}
var _ digError = errParamSingleFailed{}
func (e errParamSingleFailed) Error() string { return fmt.Sprint(e) }
func (e errParamSingleFailed) Unwrap() error { return e.Reason }
func (e errParamSingleFailed) writeMessage(w io.Writer, _ string) {
fmt.Fprintf(w, "failed to build %v", e.Key)
}
func (e errParamSingleFailed) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
func (e errParamSingleFailed) updateGraph(g *dot.Graph) {
failed := &dot.Result{
Node: &dot.Node{
Name: e.Key.name,
Group: e.Key.group,
Type: e.Key.t,
},
}
g.FailNodes([]*dot.Result{failed}, e.CtorID)
}
// errParamGroupFailed is returned when a value group cannot be built because
// any of the values in the group failed to build.
type errParamGroupFailed struct {
Key key
Reason error
CtorID dot.CtorID
}
var _ digError = errParamGroupFailed{}
func (e errParamGroupFailed) Error() string { return fmt.Sprint(e) }
func (e errParamGroupFailed) Unwrap() error { return e.Reason }
func (e errParamGroupFailed) writeMessage(w io.Writer, _ string) {
fmt.Fprintf(w, "could not build value group %v", e.Key)
}
func (e errParamGroupFailed) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
func (e errParamGroupFailed) updateGraph(g *dot.Graph) {
g.FailGroupNodes(e.Key.group, e.Key.t, e.CtorID)
}
// missingType holds information about a type that was missing in the
// container.
type missingType struct {
Key key // item that was missing
// If non-empty, we will include suggestions for what the user may have
// meant.
suggestions []key
}
// Format prints a string representation of missingType.
//
// With %v, it prints a short representation ideal for an itemized list.
//
// io.Writer
// io.Writer: did you mean *bytes.Buffer?
// io.Writer: did you mean *bytes.Buffer, or *os.File?
//
// With %+v, it prints a longer representation ideal for standalone output.
//
// io.Writer: did you mean to Provide it?
// io.Writer: did you mean to use *bytes.Buffer?
// io.Writer: did you mean to use one of *bytes.Buffer, or *os.File?
func (mt missingType) Format(w fmt.State, v rune) {
plusV := w.Flag('+') && v == 'v'
fmt.Fprint(w, mt.Key)
switch len(mt.suggestions) {
case 0:
if plusV {
io.WriteString(w, " (did you mean to Provide it?)")
}
case 1:
sug := mt.suggestions[0]
if plusV {
fmt.Fprintf(w, " (did you mean to use %v?)", sug)
} else {
fmt.Fprintf(w, " (did you mean %v?)", sug)
}
default:
if plusV {
io.WriteString(w, " (did you mean to use one of ")
} else {
io.WriteString(w, " (did you mean ")
}
lastIdx := len(mt.suggestions) - 1
for i, sug := range mt.suggestions {
if i > 0 {
io.WriteString(w, ", ")
if i == lastIdx {
io.WriteString(w, "or ")
}
}
fmt.Fprint(w, sug)
}
io.WriteString(w, "?)")
}
}
// errMissingType is returned when one or more values that were expected in
// the container were not available.
//
// Multiple instances of this error may be merged together by appending them.
type errMissingTypes []missingType // inv: len > 0
var _ digError = errMissingTypes(nil)
func newErrMissingTypes(c containerStore, k key) errMissingTypes {
// Possible types we will look for in the container. We will always look
// for pointers to the requested type and some extras on a per-Kind basis.
suggestions := []reflect.Type{reflect.PtrTo(k.t)}
if k.t.Kind() == reflect.Ptr {
// The user requested a pointer but maybe we have a value.
suggestions = append(suggestions, k.t.Elem())
}
if k.t.Kind() == reflect.Slice {
// Maybe the user meant a slice of pointers while we have the slice of elements
suggestions = append(suggestions, reflect.SliceOf(reflect.PtrTo(k.t.Elem())))
// Maybe the user meant a slice of elements while we have the slice of pointers
sliceElement := k.t.Elem()
if sliceElement.Kind() == reflect.Ptr {
suggestions = append(suggestions, reflect.SliceOf(sliceElement.Elem()))
}
}
if k.t.Kind() == reflect.Array {
// Maybe the user meant an array of pointers while we have the array of elements
suggestions = append(suggestions, reflect.ArrayOf(k.t.Len(), reflect.PtrTo(k.t.Elem())))
// Maybe the user meant an array of elements while we have the array of pointers
arrayElement := k.t.Elem()
if arrayElement.Kind() == reflect.Ptr {
suggestions = append(suggestions, reflect.ArrayOf(k.t.Len(), arrayElement.Elem()))
}
}
knownTypes := c.knownTypes()
if k.t.Kind() == reflect.Interface {
// Maybe we have an implementation of the interface.
for _, t := range knownTypes {
if t.Implements(k.t) {
suggestions = append(suggestions, t)
}
}
} else {
// Maybe we have an interface that this type implements.
for _, t := range knownTypes {
if t.Kind() == reflect.Interface {
if k.t.Implements(t) {
suggestions = append(suggestions, t)
}
}
}
}
// range through c.providers is non-deterministic. Let's sort the list of
// suggestions.
sort.Sort(byTypeName(suggestions))
mt := missingType{Key: k}
for _, t := range suggestions {
if len(c.getValueProviders(k.name, t)) > 0 {
k.t = t
mt.suggestions = append(mt.suggestions, k)
}
}
return errMissingTypes{mt}
}
func (e errMissingTypes) Error() string { return fmt.Sprint(e) }
func (e errMissingTypes) writeMessage(w io.Writer, v string) {
multiline := v == "%+v"
if len(e) == 1 {
io.WriteString(w, "missing type:")
} else {
io.WriteString(w, "missing types:")
}
if !multiline {
// With %v, we need a space between : since the error
// won't be on a new line.
io.WriteString(w, " ")
}
for i, mt := range e {
if multiline {
io.WriteString(w, "\n\t- ")
} else if i > 0 {
io.WriteString(w, "; ")
}
if multiline {
fmt.Fprintf(w, "%+v", mt)
} else {
fmt.Fprintf(w, "%v", mt)
}
}
}
func (e errMissingTypes) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
func (e errMissingTypes) updateGraph(g *dot.Graph) {
missing := make([]*dot.Result, len(e))
for i, mt := range e {
missing[i] = &dot.Result{
Node: &dot.Node{
Name: mt.Key.name,
Group: mt.Key.group,
Type: mt.Key.t,
},
}
}
g.AddMissingNodes(missing)
}
type errVisualizer interface {
updateGraph(*dot.Graph)
}

7
vendor/go.uber.org/dig/glide.yaml generated vendored Normal file
View File

@@ -0,0 +1,7 @@
package: go.uber.org/dig
license: MIT
testImport:
- package: github.com/stretchr/testify
subpackages:
- assert
- require

115
vendor/go.uber.org/dig/graph.go generated vendored Normal file
View File

@@ -0,0 +1,115 @@
// 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 "go.uber.org/dig/internal/graph"
// graphNode is a single node in the dependency graph.
type graphNode struct {
Wrapped interface{}
}
// graphHolder is the dependency graph of the container.
// It saves constructorNodes and paramGroupedSlice (value groups)
// as nodes in the graph.
// It implements the graph interface defined by internal/graph.
// It has 1-1 correspondence with the Scope whose graph it represents.
type graphHolder struct {
// all the nodes defined in the graph.
nodes []*graphNode
// Scope whose graph this holder contains.
s *Scope
// Number of nodes in the graph at last snapshot.
// -1 if no snapshot has been taken.
snap int
}
var _ graph.Graph = (*graphHolder)(nil)
func newGraphHolder(s *Scope) *graphHolder {
return &graphHolder{s: s, snap: -1}
}
func (gh *graphHolder) Order() int { return len(gh.nodes) }
// EdgesFrom returns the indices of nodes that are dependencies of node u.
//
// To do that, it needs to do one of the following:
//
// For constructor nodes, it retrieves the providers of the constructor's
// parameters from the container and reports their orders.
//
// For value group nodes, it retrieves the group providers from the container
// and reports their orders.
func (gh *graphHolder) EdgesFrom(u int) []int {
var orders []int
switch w := gh.Lookup(u).(type) {
case *constructorNode:
for _, param := range w.paramList.Params {
orders = append(orders, getParamOrder(gh, param)...)
}
case *paramGroupedSlice:
providers := gh.s.getAllGroupProviders(w.Group, w.Type.Elem())
for _, provider := range providers {
orders = append(orders, provider.Order(gh.s))
}
}
return orders
}
// NewNode adds a new value to the graph and returns its order.
func (gh *graphHolder) NewNode(wrapped interface{}) int {
order := len(gh.nodes)
gh.nodes = append(gh.nodes, &graphNode{
Wrapped: wrapped,
})
return order
}
// Lookup retrieves the value for the node with the given order.
// Lookup panics if i is invalid.
func (gh *graphHolder) Lookup(i int) interface{} {
return gh.nodes[i].Wrapped
}
// Snapshot takes a temporary snapshot of the current state of the graph.
// Use with Rollback to undo changes to the graph.
//
// Only one snapshot is allowed at a time.
// Multiple calls to snapshot will overwrite prior snapshots.
func (gh *graphHolder) Snapshot() {
gh.snap = len(gh.nodes)
}
// Rollback rolls back a snapshot to a previously captured state.
// This is a no-op if no snapshot was captured.
func (gh *graphHolder) Rollback() {
if gh.snap < 0 {
return
}
// nodes is an append-only list. To rollback, we just drop the
// extraneous entries from the slice.
gh.nodes = gh.nodes[:gh.snap]
gh.snap = -1
}

67
vendor/go.uber.org/dig/group.go generated vendored Normal file
View File

@@ -0,0 +1,67 @@
// Copyright (c) 2020 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"
"io"
"strings"
)
const (
_groupTag = "group"
)
type group struct {
Name string
Flatten bool
Soft bool
}
type errInvalidGroupOption struct{ Option string }
var _ digError = errInvalidGroupOption{}
func (e errInvalidGroupOption) Error() string { return fmt.Sprint(e) }
func (e errInvalidGroupOption) writeMessage(w io.Writer, v string) {
fmt.Fprintf(w, "invalid option %q", e.Option)
}
func (e errInvalidGroupOption) Format(w fmt.State, c rune) {
formatError(e, w, c)
}
func parseGroupString(s string) (group, error) {
components := strings.Split(s, ",")
g := group{Name: components[0]}
for _, c := range components[1:] {
switch c {
case "flatten":
g.Flatten = true
case "soft":
g.Soft = true
default:
return g, errInvalidGroupOption{Option: c}
}
}
return g, nil
}

175
vendor/go.uber.org/dig/inout.go generated vendored Normal file
View File

@@ -0,0 +1,175 @@
// Copyright (c) 2019 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 (
"container/list"
"fmt"
"reflect"
"strconv"
)
var (
_noValue reflect.Value
_errType = reflect.TypeOf((*error)(nil)).Elem()
_inPtrType = reflect.TypeOf((*In)(nil))
_inType = reflect.TypeOf(In{})
_outPtrType = reflect.TypeOf((*Out)(nil))
_outType = reflect.TypeOf(Out{})
)
// Placeholder type placed in dig.In/dig.out to make their special nature
// obvious in godocs.
// Otherwise they will appear as plain empty structs.
type digSentinel struct{}
// In may be embedded into structs to request dig to treat them as special
// parameter structs. When a constructor accepts such a struct, instead of the
// struct becoming a dependency for that constructor, all its fields become
// dependencies instead. See the section on Parameter Objects in the
// package-level documentation for more information.
//
// Fields of the struct may optionally be tagged to customize the behavior of
// dig. The following tags are supported,
//
// name Requests a value with the same name and type from the
// container. See Named Values for more information.
// optional If set to true, indicates that the dependency is optional and
// the constructor gracefully handles its absence.
// group Name of the Value Group from which this field will be filled.
// The field must be a slice type. See Value Groups in the
// package documentation for more information.
type In struct{ _ digSentinel }
// Out is an embeddable type that signals to dig that the returned
// struct should be treated differently. Instead of the struct itself
// becoming part of the container, all members of the struct will.
// Out may be embedded into structs to request dig to treat them as special
// result structs. When a constructor returns such a struct, instead of the
// struct becoming a result of the constructor, all its fields become results
// of the constructor. See the section on Result Objects in the package-level
// documentation for more information.
//
// Fields of the struct may optionally be tagged to customize the behavior of
// dig. The following tags are supported,
//
// name Specifies the name of the value. Only a field on a dig.In
// struct with the same 'name' annotation can receive this
// value. See Named Values for more information.
// group Name of the Value Group to which this field's value is being
// sent. See Value Groups in the package documentation for more
// information.
type Out struct{ _ digSentinel }
func isError(t reflect.Type) bool {
return t.Implements(_errType)
}
// IsIn checks whether the given struct is a dig.In struct. A struct qualifies
// as a dig.In struct if it embeds the dig.In type or if any struct that it
// embeds is a dig.In struct. The parameter may be the reflect.Type of the
// struct rather than the struct itself.
//
// A struct MUST qualify as a dig.In struct for its fields to be treated
// specially by dig.
//
// See the documentation for dig.In for a comprehensive list of supported
// tags.
func IsIn(o interface{}) bool {
return embedsType(o, _inType)
}
// IsOut checks whether the given struct is a dig.Out struct. A struct
// qualifies as a dig.Out struct if it embeds the dig.Out type or if any
// struct that it embeds is a dig.Out struct. The parameter may be the
// reflect.Type of the struct rather than the struct itself.
//
// A struct MUST qualify as a dig.Out struct for its fields to be treated
// specially by dig.
//
// See the documentation for dig.Out for a comprehensive list of supported
// tags.
func IsOut(o interface{}) bool {
return embedsType(o, _outType)
}
// Returns true if t embeds e or if any of the types embedded by t embed e.
func embedsType(i interface{}, e reflect.Type) bool {
// TODO: this function doesn't consider e being a pointer.
// given `type A foo { *In }`, this function would return false for
// embedding dig.In, which makes for some extra error checking in places
// that call this function. Might be worthwhile to consider reflect.Indirect
// usage to clean up the callers.
if i == nil {
return false
}
// maybe it's already a reflect.Type
t, ok := i.(reflect.Type)
if !ok {
// take the type if it's not
t = reflect.TypeOf(i)
}
// We are going to do a breadth-first search of all embedded fields.
types := list.New()
types.PushBack(t)
for types.Len() > 0 {
t := types.Remove(types.Front()).(reflect.Type)
if t == e {
return true
}
if t.Kind() != reflect.Struct {
continue
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Anonymous {
types.PushBack(f.Type)
}
}
}
// If perf is an issue, we can cache known In objects and Out objects in a
// map[reflect.Type]struct{}.
return false
}
// Checks if a field of an In struct is optional.
func isFieldOptional(f reflect.StructField) (bool, error) {
tag := f.Tag.Get(_optionalTag)
if tag == "" {
return false, nil
}
optional, err := strconv.ParseBool(tag)
if err != nil {
err = newErrInvalidInput(
fmt.Sprintf("invalid value %q for %q tag on field %v", tag, _optionalTag, f.Name), err)
}
return optional, err
}

34
vendor/go.uber.org/dig/internal/digerror/errors.go generated vendored Normal file
View File

@@ -0,0 +1,34 @@
// 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 digerror
import (
"fmt"
)
// BugPanicf panics with the provided message directing users to GitHub issues
// creation page.
func BugPanicf(msg string, args ...interface{}) {
panic(fmt.Sprintf("It looks like you have found a bug in dig. "+
"Please file an issue at https://github.com/uber-go/dig/issues/new "+
"and provide the following message: "+
msg, args...))
}

125
vendor/go.uber.org/dig/internal/digreflect/func.go generated vendored Normal file
View File

@@ -0,0 +1,125 @@
// Copyright (c) 2019 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 digreflect
import (
"fmt"
"net/url"
"reflect"
"runtime"
"strings"
)
// Func contains runtime information about a function.
type Func struct {
// Name of the function.
Name string
// Name of the package in which this function is defined.
Package string
// Path to the file in which this function is defined.
File string
// Line number in the file at which this function is defined.
Line int
}
// String returns a string representation of the function.
func (f *Func) String() string {
return fmt.Sprint(f)
}
// Format implements fmt.Formatter for Func, printing a single-line
// representation for %v and a multi-line one for %+v.
func (f *Func) Format(w fmt.State, c rune) {
if w.Flag('+') && c == 'v' {
// "path/to/package".MyFunction
// path/to/file.go:42
fmt.Fprintf(w, "%q.%v", f.Package, f.Name)
fmt.Fprintf(w, "\n\t%v:%v", f.File, f.Line)
} else {
// "path/to/package".MyFunction (path/to/file.go:42)
fmt.Fprintf(w, "%q.%v (%v:%v)", f.Package, f.Name, f.File, f.Line)
}
}
// InspectFunc inspects and returns runtime information about the given
// function.
func InspectFunc(function interface{}) *Func {
fptr := reflect.ValueOf(function).Pointer()
return InspectFuncPC(fptr)
}
// InspectFuncPC inspects and returns runtime information about the function
// at the given program counter address.
func InspectFuncPC(pc uintptr) *Func {
f := runtime.FuncForPC(pc)
if f == nil {
return nil
}
pkgName, funcName := splitFuncName(f.Name())
fileName, lineNum := f.FileLine(pc)
return &Func{
Name: funcName,
Package: pkgName,
File: fileName,
Line: lineNum,
}
}
const _vendor = "/vendor/"
func splitFuncName(function string) (pname string, fname string) {
if len(function) == 0 {
return
}
// We have something like "path.to/my/pkg.MyFunction". If the function is
// a closure, it is something like, "path.to/my/pkg.MyFunction.func1".
idx := 0
// Everything up to the first "." after the last "/" is the package name.
// Everything after the "." is the full function name.
if i := strings.LastIndex(function, "/"); i >= 0 {
idx = i
}
if i := strings.Index(function[idx:], "."); i >= 0 {
idx += i
}
pname, fname = function[:idx], function[idx+1:]
// The package may be vendored.
if i := strings.Index(pname, _vendor); i > 0 {
pname = pname[i+len(_vendor):]
}
// Package names are URL-encoded to avoid ambiguity in the case where the
// package name contains ".git". Otherwise, "foo/bar.git.MyFunction" would
// mean that "git" is the top-level function and "MyFunction" is embedded
// inside it.
if unescaped, err := url.QueryUnescape(pname); err == nil {
pname = unescaped
}
return
}

61
vendor/go.uber.org/dig/internal/dot/README.md generated vendored Normal file
View File

@@ -0,0 +1,61 @@
# Dot
The dot module generates a DOT file representation of a dependency graph.
## Interpreting the graph
The graph should be read from left to right. The leftmost node in the graph (the root node) depends
on its dependency tree to the right. An arrow from node_a to node_b in the graph means that node_b
is consumed by node_a and that node_b is a parameter of node_a. The rendered graph holds the
following kinds of nodes,
**Nodes:**
- *Constructors* [Rectangles]: Takes parameters and produces results.
- *Results* [Ovals]: Results inside a constructor are produced by that constructor. Results are consumed
directly by other constructors and/or part of a group of results.
- *Groups* [Diamonds]: Represent value groups in [fx](https://godoc.org/go.uber.org/fx). Multiple results can form a group. Any
result linked to a group by an edge are members of that group. A group is a collection of results.
Groups can also be parameters of constructors.
**Edges:**
- *Solid Arrows*: An arrow from node_a to node_b means that node_b is a parameter of node_a and that
node_a depends on node_b.
- *Dashed Arrows*: A dashed arrow from node_a to node_b represents an optional dependency that node_a
has on node_b.
**Graph Colors:**
- *Red*: Graph nodes are the root cause failures.
- *Orange*: Graph nodes are the transitive failures.
## Testing and verifying changes
Unit tests and visualize golden tests are run with
```shell
$ make test
```
You can visualize the effect of your code changes by visualizing generated test graphs as pngs.
In the dig root directory, generate the graph DOT files with respect to your latest code changes.
```shell
$ go test -generate
```
Assuming that you have [graphviz](https://www.graphviz.org/) installed and are in the testdata directory,
generate a png image representation of a graph for viewing.
```shell
$ dot -Tpng ${name_of_dot_file_in_testdata}.dot -o ${name_of_dot_file_in_testdata}.png
$ open ${name_of_dot_file_in_testdata}.png
```
## Graph Pruning
If dot.Visualize is used to visualize an error graph, non-failing nodes are pruned out of the graph
to make the error graph more readable to the user. Pruning increases readability since successful
nodes clutter the graph and do not help the user debug errors.

466
vendor/go.uber.org/dig/internal/dot/graph.go generated vendored Normal file
View File

@@ -0,0 +1,466 @@
// Copyright (c) 2019 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 dot
import (
"fmt"
"reflect"
)
// ErrorType of a constructor or group is updated when they fail to build.
type ErrorType int
const (
noError ErrorType = iota
rootCause
transitiveFailure
)
// CtorID is a unique numeric identifier for constructors.
type CtorID uintptr
// Ctor encodes a constructor provided to the container for the DOT graph.
type Ctor struct {
Name string
Package string
File string
Line int
ID CtorID
Params []*Param
GroupParams []*Group
Results []*Result
ErrorType ErrorType
}
// removeParam deletes the dependency on the provided result's nodeKey.
// This is used to prune links to results of deleted constructors.
func (c *Ctor) removeParam(k nodeKey) {
var pruned []*Param
for _, p := range c.Params {
if k != p.nodeKey() {
pruned = append(pruned, p)
}
}
c.Params = pruned
}
type nodeKey struct {
t reflect.Type
name string
group string
}
// Node is a single node in a graph and is embedded into Params and Results.
type Node struct {
Type reflect.Type
Name string
Group string
}
func (n *Node) nodeKey() nodeKey {
return nodeKey{t: n.Type, name: n.Name, group: n.Group}
}
// Param is a parameter node in the graph. Parameters are the input to constructors.
type Param struct {
*Node
Optional bool
}
// Result is a result node in the graph. Results are the output of constructors.
type Result struct {
*Node
// GroupIndex is added to differentiate grouped values from one another.
// Since grouped values have the same type and group, their Node / string
// representations are the same so we need indices to uniquely identify
// the values.
GroupIndex int
}
// Group is a group node in the graph. Group represents an fx value group.
type Group struct {
// Type is the type of values in the group.
Type reflect.Type
Name string
Results []*Result
ErrorType ErrorType
}
func (g *Group) nodeKey() nodeKey {
return nodeKey{t: g.Type, group: g.Name}
}
// TODO(rhang): Avoid linear search to discover group results that should be pruned.
func (g *Group) removeResult(r *Result) {
var pruned []*Result
for _, rg := range g.Results {
if r.GroupIndex != rg.GroupIndex {
pruned = append(pruned, rg)
}
}
g.Results = pruned
}
// Graph is the DOT-format graph in a Container.
type Graph struct {
Ctors []*Ctor
ctorMap map[CtorID]*Ctor
Groups []*Group
groupMap map[nodeKey]*Group
consumers map[nodeKey][]*Ctor
Failed *FailedNodes
}
// FailedNodes is the nodes that failed in the graph.
type FailedNodes struct {
// RootCauses is a list of the point of failures. They are the root causes
// of failed invokes and can be either missing types (not provided) or
// error types (error providing).
RootCauses []*Result
// TransitiveFailures is the list of nodes that failed to build due to
// missing/failed dependencies.
TransitiveFailures []*Result
// ctors is a collection of failed constructors IDs that are populated as the graph is
// traversed for errors.
ctors map[CtorID]struct{}
// Groups is a collection of failed groupKeys that is populated as the graph is traversed
// for errors.
groups map[nodeKey]struct{}
}
// NewGraph creates an empty graph.
func NewGraph() *Graph {
return &Graph{
ctorMap: make(map[CtorID]*Ctor),
groupMap: make(map[nodeKey]*Group),
consumers: make(map[nodeKey][]*Ctor),
Failed: &FailedNodes{
ctors: make(map[CtorID]struct{}),
groups: make(map[nodeKey]struct{}),
},
}
}
// NewGroup creates a new group with information in the groupKey.
func NewGroup(k nodeKey) *Group {
return &Group{
Type: k.t,
Name: k.group,
}
}
// AddCtor adds the constructor with paramList and resultList into the graph.
func (dg *Graph) AddCtor(c *Ctor, paramList []*Param, resultList []*Result) {
var (
params []*Param
groupParams []*Group
)
// Loop through the paramList to separate them into regular params and
// grouped params. For grouped params, we use getGroup to find the actual
// group.
for _, param := range paramList {
if param.Group == "" {
// Not a value group.
params = append(params, param)
continue
}
k := nodeKey{t: param.Type.Elem(), group: param.Group}
group := dg.getGroup(k)
groupParams = append(groupParams, group)
}
for _, result := range resultList {
// If the result is a grouped value, we want to update its GroupIndex
// and add it to the Group.
if result.Group != "" {
dg.addToGroup(result, c.ID)
}
}
c.Params = params
c.GroupParams = groupParams
c.Results = resultList
// Track which constructors consume a parameter.
for _, p := range paramList {
k := p.nodeKey()
dg.consumers[k] = append(dg.consumers[k], c)
}
dg.Ctors = append(dg.Ctors, c)
dg.ctorMap[c.ID] = c
}
func (dg *Graph) failNode(r *Result, isRootCause bool) {
if isRootCause {
dg.addRootCause(r)
} else {
dg.addTransitiveFailure(r)
}
}
// AddMissingNodes adds missing nodes to the list of failed Results in the graph.
func (dg *Graph) AddMissingNodes(results []*Result) {
// The failure(s) are root causes if there are no other failures.
isRootCause := len(dg.Failed.RootCauses) == 0
for _, r := range results {
dg.failNode(r, isRootCause)
}
}
// FailNodes adds results to the list of failed Results in the graph, and
// updates the state of the constructor with the given id accordingly.
func (dg *Graph) FailNodes(results []*Result, id CtorID) {
// This failure is the root cause if there are no other failures.
isRootCause := len(dg.Failed.RootCauses) == 0
dg.Failed.ctors[id] = struct{}{}
for _, r := range results {
dg.failNode(r, isRootCause)
}
if c, ok := dg.ctorMap[id]; ok {
if isRootCause {
c.ErrorType = rootCause
} else {
c.ErrorType = transitiveFailure
}
}
}
// FailGroupNodes finds and adds the failed grouped nodes to the list of failed
// Results in the graph, and updates the state of the group and constructor
// with the given id accordingly.
func (dg *Graph) FailGroupNodes(name string, t reflect.Type, id CtorID) {
// This failure is the root cause if there are no other failures.
isRootCause := len(dg.Failed.RootCauses) == 0
k := nodeKey{t: t, group: name}
group := dg.getGroup(k)
// If the ctor does not exist it cannot be failed.
if _, ok := dg.ctorMap[id]; !ok {
return
}
// Track which constructors and groups have failed.
dg.Failed.ctors[id] = struct{}{}
dg.Failed.groups[k] = struct{}{}
for _, r := range dg.ctorMap[id].Results {
if r.Type == t && r.Group == name {
dg.failNode(r, isRootCause)
}
}
if c, ok := dg.ctorMap[id]; ok {
if isRootCause {
group.ErrorType = rootCause
c.ErrorType = rootCause
} else {
group.ErrorType = transitiveFailure
c.ErrorType = transitiveFailure
}
}
}
// getGroup finds the group by nodeKey from the graph. If it is not available,
// a new group is created and returned.
func (dg *Graph) getGroup(k nodeKey) *Group {
g, ok := dg.groupMap[k]
if !ok {
g = NewGroup(k)
dg.groupMap[k] = g
dg.Groups = append(dg.Groups, g)
}
return g
}
// addToGroup adds a newly provided grouped result to the appropriate group.
func (dg *Graph) addToGroup(r *Result, id CtorID) {
k := nodeKey{t: r.Type, group: r.Group}
group := dg.getGroup(k)
r.GroupIndex = len(group.Results)
group.Results = append(group.Results, r)
}
// PruneSuccess removes elements from the graph that do not have failed results.
// Removing elements that do not have failing results makes the graph easier to debug,
// since non-failing nodes and edges can clutter the graph and don't help the user debug.
func (dg *Graph) PruneSuccess() {
dg.pruneCtors(dg.Failed.ctors)
dg.pruneGroups(dg.Failed.groups)
}
// pruneCtors removes constructors from the graph that do not have failing Results.
func (dg *Graph) pruneCtors(failed map[CtorID]struct{}) {
var pruned []*Ctor
for _, c := range dg.Ctors {
if _, ok := failed[c.ID]; ok {
pruned = append(pruned, c)
continue
}
// If a constructor is deleted, the constructor's stale result references need to
// be removed from that result's Group and/or consuming constructor.
dg.pruneCtorParams(c, dg.consumers)
dg.pruneGroupResults(c, dg.groupMap)
delete(dg.ctorMap, c.ID)
}
dg.Ctors = pruned
}
// pruneGroups removes groups from the graph that do not have failing results.
func (dg *Graph) pruneGroups(failed map[nodeKey]struct{}) {
var pruned []*Group
for _, g := range dg.Groups {
k := g.nodeKey()
if _, ok := failed[k]; ok {
pruned = append(pruned, g)
continue
}
delete(dg.groupMap, k)
}
dg.Groups = pruned
dg.pruneCtorGroupParams(dg.groupMap)
}
// pruneCtorParams removes results of the constructor argument that are still referenced in the
// Params of constructors that consume those results. If the results in the constructor are found
// in the params of a consuming constructor that result should be removed.
func (dg *Graph) pruneCtorParams(c *Ctor, consumers map[nodeKey][]*Ctor) {
for _, r := range c.Results {
for _, ctor := range consumers[r.nodeKey()] {
ctor.removeParam(r.nodeKey())
}
}
}
// pruneCtorGroupParams removes constructor results that are still referenced in the GroupParams of
// constructors that consume those results.
func (dg *Graph) pruneCtorGroupParams(groups map[nodeKey]*Group) {
for _, c := range dg.Ctors {
var pruned []*Group
for _, gp := range c.GroupParams {
k := gp.nodeKey()
if _, ok := groups[k]; ok {
pruned = append(pruned, gp)
}
}
c.GroupParams = pruned
}
}
// pruneGroupResults removes results of the constructor argument that are still referenced in
// the Group object that contains that result. If a group no longer exists references to that
// should should be removed.
func (dg *Graph) pruneGroupResults(c *Ctor, groups map[nodeKey]*Group) {
for _, r := range c.Results {
k := r.nodeKey()
if k.group == "" {
continue
}
g, ok := groups[k]
if ok {
g.removeResult(r)
}
}
}
// String implements fmt.Stringer for Param.
func (p *Param) String() string {
if p.Name != "" {
return fmt.Sprintf("%v[name=%v]", p.Type.String(), p.Name)
}
return p.Type.String()
}
// String implements fmt.Stringer for Result.
func (r *Result) String() string {
switch {
case r.Name != "":
return fmt.Sprintf("%v[name=%v]", r.Type.String(), r.Name)
case r.Group != "":
return fmt.Sprintf("%v[group=%v]%v", r.Type.String(), r.Group, r.GroupIndex)
default:
return r.Type.String()
}
}
// String implements fmt.Stringer for Group.
func (g *Group) String() string {
return fmt.Sprintf("[type=%v group=%v]", g.Type.String(), g.Name)
}
// Attributes composes and returns a string of the Result node's attributes.
func (r *Result) Attributes() string {
switch {
case r.Name != "":
return fmt.Sprintf(`label=<%v<BR /><FONT POINT-SIZE="10">Name: %v</FONT>>`, r.Type, r.Name)
case r.Group != "":
return fmt.Sprintf(`label=<%v<BR /><FONT POINT-SIZE="10">Group: %v</FONT>>`, r.Type, r.Group)
default:
return fmt.Sprintf(`label=<%v>`, r.Type)
}
}
// Attributes composes and returns a string of the Group node's attributes.
func (g *Group) Attributes() string {
attr := fmt.Sprintf(`shape=diamond label=<%v<BR /><FONT POINT-SIZE="10">Group: %v</FONT>>`, g.Type, g.Name)
if g.ErrorType != noError {
attr += " color=" + g.ErrorType.Color()
}
return attr
}
// Color returns the color representation of each ErrorType.
func (s ErrorType) Color() string {
switch s {
case rootCause:
return "red"
case transitiveFailure:
return "orange"
default:
return "black"
}
}
func (dg *Graph) addRootCause(r *Result) {
dg.Failed.RootCauses = append(dg.Failed.RootCauses, r)
}
func (dg *Graph) addTransitiveFailure(r *Result) {
dg.Failed.TransitiveFailures = append(dg.Failed.TransitiveFailures, r)
}

118
vendor/go.uber.org/dig/internal/graph/graph.go generated vendored Normal file
View File

@@ -0,0 +1,118 @@
// 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 graph
// Graph represents a simple interface for representation
// of a directed graph.
// It is assumed that each node in the graph is uniquely
// identified with an incremental positive integer (i.e. 1, 2, 3...).
// A value of 0 for a node represents a sentinel error value.
type Graph interface {
// Order returns the total number of nodes in the graph
Order() int
// EdgesFrom returns a list of integers that each
// represents a node that has an edge from node u.
EdgesFrom(u int) []int
}
// IsAcyclic uses depth-first search to find cycles
// in a generic graph represented by Graph interface.
// If a cycle is found, it returns a list of nodes that
// are in the cyclic path, identified by their orders.
func IsAcyclic(g Graph) (bool, []int) {
// cycleStart is a node that introduces a cycle in
// the graph. Values in the range [1, g.Order()) mean
// that there exists a cycle in g.
info := newCycleInfo(g.Order())
for i := 0; i < g.Order(); i++ {
info.Reset()
cycle := isAcyclic(g, i, info, nil /* cycle path */)
if len(cycle) > 0 {
return false, cycle
}
}
return true, nil
}
// isAcyclic traverses the given graph starting from a specific node
// using depth-first search using recursion. If a cycle is detected,
// it returns the node that contains the "last" edge that introduces
// a cycle.
// For example, running isAcyclic starting from 1 on the following
// graph will return 3.
//
// 1 -> 2 -> 3 -> 1
func isAcyclic(g Graph, u int, info cycleInfo, path []int) []int {
// We've already verified that there are no cycles from this node.
if info[u].Visited {
return nil
}
info[u].Visited = true
info[u].OnStack = true
path = append(path, u)
for _, v := range g.EdgesFrom(u) {
if !info[v].Visited {
if cycle := isAcyclic(g, v, info, path); len(cycle) > 0 {
return cycle
}
} else if info[v].OnStack {
// We've found a cycle, and we have a full path back.
// Prune it down to just the cyclic nodes.
cycle := path
for i := len(cycle) - 1; i >= 0; i-- {
if cycle[i] == v {
cycle = cycle[i:]
break
}
}
// Complete the cycle by adding this node to it.
return append(cycle, v)
}
}
info[u].OnStack = false
return nil
}
// cycleNode keeps track of a single node's info for cycle detection.
type cycleNode struct {
Visited bool
OnStack bool
}
// cycleInfo contains information about each node while we're trying to find
// cycles.
type cycleInfo []cycleNode
func newCycleInfo(order int) cycleInfo {
return make(cycleInfo, order)
}
func (info cycleInfo) Reset() {
for i := range info {
info[i].OnStack = false
}
}

211
vendor/go.uber.org/dig/invoke.go generated vendored Normal file
View File

@@ -0,0 +1,211 @@
// 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"
"go.uber.org/dig/internal/digreflect"
"go.uber.org/dig/internal/graph"
"reflect"
)
// An InvokeOption modifies the default behavior of Invoke.
type InvokeOption interface {
applyInvokeOption(*invokeOptions)
}
type invokeOptions struct {
Info *InvokeInfo
}
// InvokeInfo provides information about an Invoke.
type InvokeInfo struct {
Inputs []*Input
}
// FillInvokeInfo is an InvokeOption that writes information on the types
// accepted by the Invoke function into the specified InvokeInfo.
// For example:
//
// var info dig.InvokeInfo
// err := c.Invoke(func(string, int){}, dig.FillInvokeInfo(&info))
//
// info.Inputs[0].String() will be string.
// info.Inputs[1].String() will be int.
func FillInvokeInfo(info *InvokeInfo) InvokeOption {
return fillInvokeInfoOption{info: info}
}
type fillInvokeInfoOption struct {
info *InvokeInfo
}
func (o fillInvokeInfoOption) String() string {
return fmt.Sprintf("FillInvokeInfo(%p)", o.info)
}
func (o fillInvokeInfoOption) applyInvokeOption(opts *invokeOptions) {
opts.Info = o.info
}
// Invoke runs the given function after instantiating its dependencies.
//
// Any arguments that the function has are treated as its dependencies. The
// dependencies are instantiated in an unspecified order along with any
// dependencies that they might have.
//
// The function may return an error to indicate failure. The error will be
// returned to the caller as-is.
//
// If the [RecoverFromPanics] option was given to the container and a panic
// occurs when invoking, a [PanicError] with the panic contained will be
// returned. See [PanicError] for more info.
func (c *Container) Invoke(function interface{}, opts ...InvokeOption) error {
return c.scope.Invoke(function, opts...)
}
// Invoke runs the given function after instantiating its dependencies.
//
// Any arguments that the function has are treated as its dependencies. The
// dependencies are instantiated in an unspecified order along with any
// dependencies that they might have.
//
// The function may return an error to indicate failure. The error will be
// returned to the caller as-is.
func (s *Scope) Invoke(function interface{}, opts ...InvokeOption) (err error) {
ftype := reflect.TypeOf(function)
if ftype == nil {
return newErrInvalidInput("can't invoke an untyped nil", nil)
}
if ftype.Kind() != reflect.Func {
return newErrInvalidInput(
fmt.Sprintf("can't invoke non-function %v (type %v)", function, ftype), nil)
}
pl, err := newParamList(ftype, s)
if err != nil {
return err
}
if err := shallowCheckDependencies(s, pl); err != nil {
return errMissingDependencies{
Func: digreflect.InspectFunc(function),
Reason: err,
}
}
if !s.isVerifiedAcyclic {
if ok, cycle := graph.IsAcyclic(s.gh); !ok {
return newErrInvalidInput("cycle detected in dependency graph", s.cycleDetectedError(cycle))
}
s.isVerifiedAcyclic = true
}
args, err := pl.BuildList(s)
if err != nil {
return errArgumentsFailed{
Func: digreflect.InspectFunc(function),
Reason: err,
}
}
if s.recoverFromPanics {
defer func() {
if p := recover(); p != nil {
err = PanicError{
fn: digreflect.InspectFunc(function),
Panic: p,
}
}
}()
}
var options invokeOptions
for _, o := range opts {
o.applyInvokeOption(&options)
}
// Record info for the invoke if requested
if info := options.Info; info != nil {
params := pl.DotParam()
info.Inputs = make([]*Input, len(params))
for i, p := range params {
info.Inputs[i] = &Input{
t: p.Type,
optional: p.Optional,
name: p.Name,
group: p.Group,
}
}
}
returned := s.invokerFn(reflect.ValueOf(function), args)
if len(returned) == 0 {
return nil
}
if last := returned[len(returned)-1]; isError(last.Type()) {
if err, _ := last.Interface().(error); err != nil {
return err
}
}
return nil
}
// Checks that all direct dependencies of the provided parameters are present in
// the container. Returns an error if not.
func shallowCheckDependencies(c containerStore, pl paramList) error {
var err errMissingTypes
missingDeps := findMissingDependencies(c, pl.Params...)
for _, dep := range missingDeps {
err = append(err, newErrMissingTypes(c, key{name: dep.Name, t: dep.Type})...)
}
if len(err) > 0 {
return err
}
return nil
}
func findMissingDependencies(c containerStore, params ...param) []paramSingle {
var missingDeps []paramSingle
for _, param := range params {
switch p := param.(type) {
case paramSingle:
allProviders := c.getAllValueProviders(p.Name, p.Type)
_, hasDecoratedValue := c.getDecoratedValue(p.Name, p.Type)
// This means that there is no provider that provides this value,
// and it is NOT being decorated and is NOT optional.
// In the case that there is no providers but there is a decorated value
// of this type, it can be provided safely so we can safely skip this.
if len(allProviders) == 0 && !hasDecoratedValue && !p.Optional {
missingDeps = append(missingDeps, p)
}
case paramObject:
for _, f := range p.Fields {
missingDeps = append(missingDeps, findMissingDependencies(c, f.Param)...)
}
}
}
return missingDeps
}

668
vendor/go.uber.org/dig/param.go generated vendored Normal file
View File

@@ -0,0 +1,668 @@
// Copyright (c) 2019-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"
"reflect"
"strconv"
"strings"
"go.uber.org/dig/internal/digerror"
"go.uber.org/dig/internal/dot"
)
// The param interface represents a dependency for a constructor.
//
// The following implementations exist:
//
// paramList All arguments of the constructor.
// paramSingle An explicitly requested type.
// paramObject dig.In struct where each field in the struct can be another
// param.
// paramGroupedSlice
// A slice consuming a value group. This will receive all
// values produced with a `group:".."` tag with the same name
// as a slice.
type param interface {
fmt.Stringer
// Build this dependency and any of its dependencies from the provided
// Container.
//
// This MAY panic if the param does not produce a single value.
Build(store containerStore) (reflect.Value, error)
// DotParam returns a slice of dot.Param(s).
DotParam() []*dot.Param
}
var (
_ param = paramSingle{}
_ param = paramObject{}
_ param = paramList{}
_ param = paramGroupedSlice{}
)
// newParam builds a param from the given type. If the provided type is a
// dig.In struct, an paramObject will be returned.
func newParam(t reflect.Type, c containerStore) (param, error) {
switch {
case IsOut(t) || (t.Kind() == reflect.Ptr && IsOut(t.Elem())) || embedsType(t, _outPtrType):
return nil, newErrInvalidInput(fmt.Sprintf(
"cannot depend on result objects: %v embeds a dig.Out", t), nil)
case IsIn(t):
return newParamObject(t, c)
case embedsType(t, _inPtrType):
return nil, newErrInvalidInput(fmt.Sprintf(
"cannot build a parameter object by embedding *dig.In, embed dig.In instead: %v embeds *dig.In", t), nil)
case t.Kind() == reflect.Ptr && IsIn(t.Elem()):
return nil, newErrInvalidInput(fmt.Sprintf(
"cannot depend on a pointer to a parameter object, use a value instead: %v is a pointer to a struct that embeds dig.In", t), nil)
default:
return paramSingle{Type: t}, nil
}
}
// paramList holds all arguments of the constructor as params.
//
// NOTE: Build() MUST NOT be called on paramList. Instead, BuildList
// must be called.
type paramList struct {
ctype reflect.Type // type of the constructor
Params []param
}
func (pl paramList) DotParam() []*dot.Param {
var types []*dot.Param
for _, param := range pl.Params {
types = append(types, param.DotParam()...)
}
return types
}
func (pl paramList) String() string {
args := make([]string, len(pl.Params))
for i, p := range pl.Params {
args[i] = p.String()
}
return fmt.Sprint(args)
}
// newParamList builds a paramList from the provided constructor type.
//
// Variadic arguments of a constructor are ignored and not included as
// dependencies.
func newParamList(ctype reflect.Type, c containerStore) (paramList, error) {
numArgs := ctype.NumIn()
if ctype.IsVariadic() {
// NOTE: If the function is variadic, we skip the last argument
// because we're not filling variadic arguments yet. See #120.
numArgs--
}
pl := paramList{
ctype: ctype,
Params: make([]param, 0, numArgs),
}
for i := 0; i < numArgs; i++ {
p, err := newParam(ctype.In(i), c)
if err != nil {
return pl, newErrInvalidInput(fmt.Sprintf("bad argument %d", i+1), err)
}
pl.Params = append(pl.Params, p)
}
return pl, nil
}
func (pl paramList) Build(containerStore) (reflect.Value, error) {
digerror.BugPanicf("paramList.Build() must never be called")
panic("") // Unreachable, as BugPanicf above will panic.
}
// BuildList returns an ordered list of values which may be passed directly
// to the underlying constructor.
func (pl paramList) BuildList(c containerStore) ([]reflect.Value, error) {
args := make([]reflect.Value, len(pl.Params))
for i, p := range pl.Params {
var err error
args[i], err = p.Build(c)
if err != nil {
return nil, err
}
}
return args, nil
}
// paramSingle is an explicitly requested type, optionally with a name.
//
// This object must be present in the graph as-is unless it's specified as
// optional.
type paramSingle struct {
Name string
Optional bool
Type reflect.Type
}
func (ps paramSingle) DotParam() []*dot.Param {
return []*dot.Param{
{
Node: &dot.Node{
Type: ps.Type,
Name: ps.Name,
},
Optional: ps.Optional,
},
}
}
func (ps paramSingle) String() string {
// tally.Scope[optional] means optional
// tally.Scope[optional, name="foo"] means named optional
var opts []string
if ps.Optional {
opts = append(opts, "optional")
}
if ps.Name != "" {
opts = append(opts, fmt.Sprintf("name=%q", ps.Name))
}
if len(opts) == 0 {
return fmt.Sprint(ps.Type)
}
return fmt.Sprintf("%v[%v]", ps.Type, strings.Join(opts, ", "))
}
// search the given container and its ancestors for a decorated value.
func (ps paramSingle) getDecoratedValue(c containerStore) (reflect.Value, bool) {
for _, c := range c.storesToRoot() {
if v, ok := c.getDecoratedValue(ps.Name, ps.Type); ok {
return v, ok
}
}
return _noValue, false
}
// builds the parameter using decorators in all scopes that affect the
// current scope, if there are any. If there are multiple Scopes that decorates
// this parameter, the closest one to the Scope that invoked this will be used.
// If there are no decorators associated with this parameter, _noValue is returned.
func (ps paramSingle) buildWithDecorators(c containerStore) (v reflect.Value, found bool, err error) {
var (
d decorator
decoratingScope containerStore
)
stores := c.storesToRoot()
for _, s := range stores {
if d, found = s.getValueDecorator(ps.Name, ps.Type); !found {
continue
}
if d.State() == decoratorOnStack {
// This decorator is already being run.
// Avoid a cycle and look further.
d = nil
continue
}
decoratingScope = s
break
}
if !found || d == nil {
return _noValue, false, nil
}
if err = d.Call(decoratingScope); err != nil {
v, err = _noValue, errParamSingleFailed{
CtorID: 1,
Key: key{t: ps.Type, name: ps.Name},
Reason: err,
}
return v, found, err
}
v, _ = decoratingScope.getDecoratedValue(ps.Name, ps.Type)
return
}
func (ps paramSingle) Build(c containerStore) (reflect.Value, error) {
v, found, err := ps.buildWithDecorators(c)
if found {
return v, err
}
// Check whether the value is a decorated value first.
if v, ok := ps.getDecoratedValue(c); ok {
return v, nil
}
// Starting at the given container and working our way up its parents,
// find one that provides this dependency.
//
// Once found, we'll use that container for the rest of the invocation.
// Dependencies of this type will begin searching at that container,
// rather than starting at base.
var providers []provider
var providingContainer containerStore
for _, container := range c.storesToRoot() {
// first check if the scope already has cached a value for the type.
if v, ok := container.getValue(ps.Name, ps.Type); ok {
return v, nil
}
providers = container.getValueProviders(ps.Name, ps.Type)
if len(providers) > 0 {
providingContainer = container
break
}
}
if len(providers) == 0 {
if ps.Optional {
return reflect.Zero(ps.Type), nil
}
return _noValue, newErrMissingTypes(c, key{name: ps.Name, t: ps.Type})
}
for _, n := range providers {
err := n.Call(n.OrigScope())
if err == nil {
continue
}
// If we're missing dependencies but the parameter itself is optional,
// we can just move on.
if _, ok := err.(errMissingDependencies); ok && ps.Optional {
return reflect.Zero(ps.Type), nil
}
return _noValue, errParamSingleFailed{
CtorID: n.ID(),
Key: key{t: ps.Type, name: ps.Name},
Reason: err,
}
}
// If we get here, it's impossible for the value to be absent from the
// container.
v, _ = providingContainer.getValue(ps.Name, ps.Type)
return v, nil
}
// paramObject is a dig.In struct where each field is another param.
//
// This object is not expected in the graph as-is.
type paramObject struct {
Type reflect.Type
Fields []paramObjectField
FieldOrders []int
}
func (po paramObject) DotParam() []*dot.Param {
var types []*dot.Param
for _, field := range po.Fields {
types = append(types, field.DotParam()...)
}
return types
}
func (po paramObject) String() string {
fields := make([]string, len(po.Fields))
for i, f := range po.Fields {
fields[i] = f.Param.String()
}
return strings.Join(fields, " ")
}
// getParamOrder returns the order(s) of a parameter type.
func getParamOrder(gh *graphHolder, param param) []int {
var orders []int
switch p := param.(type) {
case paramSingle:
providers := gh.s.getAllValueProviders(p.Name, p.Type)
for _, provider := range providers {
orders = append(orders, provider.Order(gh.s))
}
case paramGroupedSlice:
// value group parameters have nodes of their own.
// We can directly return that here.
orders = append(orders, p.orders[gh.s])
case paramObject:
for _, pf := range p.Fields {
orders = append(orders, getParamOrder(gh, pf.Param)...)
}
}
return orders
}
// newParamObject builds an paramObject from the provided type. The type MUST
// be a dig.In struct.
func newParamObject(t reflect.Type, c containerStore) (paramObject, error) {
po := paramObject{Type: t}
// Check if the In type supports ignoring unexported fields.
var ignoreUnexported bool
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Type == _inType {
var err error
ignoreUnexported, err = isIgnoreUnexportedSet(f)
if err != nil {
return po, err
}
break
}
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Type == _inType {
// Skip over the dig.In embed.
continue
}
if f.PkgPath != "" && ignoreUnexported {
// Skip over an unexported field if it is allowed.
continue
}
pof, err := newParamObjectField(i, f, c)
if err != nil {
return po, newErrInvalidInput(
fmt.Sprintf("bad field %q of %v", f.Name, t), err)
}
po.Fields = append(po.Fields, pof)
}
return po, nil
}
func (po paramObject) Build(c containerStore) (reflect.Value, error) {
dest := reflect.New(po.Type).Elem()
// We have to build soft groups after all other fields, to avoid cases
// when a field calls a provider for a soft value group, but the value is
// not provided to it because the value group is declared before the field
var softGroupsQueue []paramObjectField
var fields []paramObjectField
for _, f := range po.Fields {
if p, ok := f.Param.(paramGroupedSlice); ok && p.Soft {
softGroupsQueue = append(softGroupsQueue, f)
continue
}
fields = append(fields, f)
}
fields = append(fields, softGroupsQueue...)
for _, f := range fields {
v, err := f.Build(c)
if err != nil {
return dest, err
}
dest.Field(f.FieldIndex).Set(v)
}
return dest, nil
}
// paramObjectField is a single field of a dig.In struct.
type paramObjectField struct {
// Name of the field in the struct.
FieldName string
// Index of this field in the target struct.
//
// We need to track this separately because not all fields of the
// struct map to params.
FieldIndex int
// The dependency requested by this field.
Param param
}
func (pof paramObjectField) DotParam() []*dot.Param {
return pof.Param.DotParam()
}
func newParamObjectField(idx int, f reflect.StructField, c containerStore) (paramObjectField, error) {
pof := paramObjectField{
FieldName: f.Name,
FieldIndex: idx,
}
var p param
switch {
case f.PkgPath != "":
return pof, newErrInvalidInput(
fmt.Sprintf("unexported fields not allowed in dig.In, did you mean to export %q (%v)?", f.Name, f.Type), nil)
case f.Tag.Get(_groupTag) != "":
var err error
p, err = newParamGroupedSlice(f, c)
if err != nil {
return pof, err
}
default:
var err error
p, err = newParam(f.Type, c)
if err != nil {
return pof, err
}
}
if ps, ok := p.(paramSingle); ok {
ps.Name = f.Tag.Get(_nameTag)
var err error
ps.Optional, err = isFieldOptional(f)
if err != nil {
return pof, err
}
p = ps
}
pof.Param = p
return pof, nil
}
func (pof paramObjectField) Build(c containerStore) (reflect.Value, error) {
v, err := pof.Param.Build(c)
if err != nil {
return v, err
}
return v, nil
}
// paramGroupedSlice is a param which produces a slice of values with the same
// group name.
type paramGroupedSlice struct {
// Name of the group as specified in the `group:".."` tag.
Group string
// Type of the slice.
Type reflect.Type
// Soft is used to denote a soft dependency between this param and its
// constructors, if it's true its constructors are only called if they
// provide another value requested in the graph
Soft bool
orders map[*Scope]int
}
func (pt paramGroupedSlice) String() string {
// io.Reader[group="foo"] refers to a group of io.Readers called 'foo'
return fmt.Sprintf("%v[group=%q]", pt.Type.Elem(), pt.Group)
}
func (pt paramGroupedSlice) DotParam() []*dot.Param {
return []*dot.Param{
{
Node: &dot.Node{
Type: pt.Type,
Group: pt.Group,
},
},
}
}
// newParamGroupedSlice builds a paramGroupedSlice from the provided type with
// the given name.
//
// The type MUST be a slice type.
func newParamGroupedSlice(f reflect.StructField, c containerStore) (paramGroupedSlice, error) {
g, err := parseGroupString(f.Tag.Get(_groupTag))
if err != nil {
return paramGroupedSlice{}, err
}
pg := paramGroupedSlice{
Group: g.Name,
Type: f.Type,
orders: make(map[*Scope]int),
Soft: g.Soft,
}
name := f.Tag.Get(_nameTag)
optional, _ := isFieldOptional(f)
switch {
case f.Type.Kind() != reflect.Slice:
return pg, newErrInvalidInput(
fmt.Sprintf("value groups may be consumed as slices only: field %q (%v) is not a slice", f.Name, f.Type), nil)
case g.Flatten:
return pg, newErrInvalidInput(
fmt.Sprintf("cannot use flatten in parameter value groups: field %q (%v) specifies flatten", f.Name, f.Type), nil)
case name != "":
return pg, newErrInvalidInput(
fmt.Sprintf("cannot use named values with value groups: name:%q requested with group:%q", name, pg.Group), nil)
case optional:
return pg, newErrInvalidInput("value groups cannot be optional", nil)
}
c.newGraphNode(&pg, pg.orders)
return pg, nil
}
// retrieves any decorated values that may be committed in this scope, or
// any of the parent Scopes. In the case where there are multiple scopes that
// are decorating the same type, the closest scope in effect will be replacing
// any decorated value groups provided in further scopes.
func (pt paramGroupedSlice) getDecoratedValues(c containerStore) (reflect.Value, bool) {
for _, c := range c.storesToRoot() {
if items, ok := c.getDecoratedValueGroup(pt.Group, pt.Type); ok {
return items, true
}
}
return _noValue, false
}
// search the given container and its parents for matching group decorators
// and call them to commit values. If any decorators return an error,
// that error is returned immediately. If all decorators succeeds, nil is returned.
// The order in which the decorators are invoked is from the top level scope to
// the current scope, to account for decorators that decorate values that were
// already decorated.
func (pt paramGroupedSlice) callGroupDecorators(c containerStore) error {
stores := c.storesToRoot()
for i := len(stores) - 1; i >= 0; i-- {
c := stores[i]
if d, found := c.getGroupDecorator(pt.Group, pt.Type.Elem()); found {
if d.State() == decoratorOnStack {
// This decorator is already being run. Avoid cycle
// and look further.
continue
}
if err := d.Call(c); err != nil {
return errParamGroupFailed{
CtorID: d.ID(),
Key: key{group: pt.Group, t: pt.Type.Elem()},
Reason: err,
}
}
}
}
return nil
}
// search the given container and its parent for matching group providers and
// call them to commit values. If an error is encountered, return the number
// of providers called and a non-nil error from the first provided.
func (pt paramGroupedSlice) callGroupProviders(c containerStore) (int, error) {
itemCount := 0
for _, c := range c.storesToRoot() {
providers := c.getGroupProviders(pt.Group, pt.Type.Elem())
itemCount += len(providers)
for _, n := range providers {
if err := n.Call(n.OrigScope()); err != nil {
return 0, errParamGroupFailed{
CtorID: n.ID(),
Key: key{group: pt.Group, t: pt.Type.Elem()},
Reason: err,
}
}
}
}
return itemCount, nil
}
func (pt paramGroupedSlice) Build(c containerStore) (reflect.Value, error) {
// do not call this if we are already inside a decorator since
// it will result in an infinite recursion. (i.e. decorate -> params.BuildList() -> Decorate -> params.BuildList...)
// this is safe since a value can be decorated at most once in a given scope.
if err := pt.callGroupDecorators(c); err != nil {
return _noValue, err
}
// Check if we have decorated values
if decoratedItems, ok := pt.getDecoratedValues(c); ok {
return decoratedItems, nil
}
// If we do not have any decorated values and the group isn't soft,
// find the providers and call them.
itemCount := 0
if !pt.Soft {
var err error
itemCount, err = pt.callGroupProviders(c)
if err != nil {
return _noValue, err
}
}
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())...)
}
return result, nil
}
// Checks if ignoring unexported files in an In struct is allowed.
// The struct field MUST be an _inType.
func isIgnoreUnexportedSet(f reflect.StructField) (bool, error) {
tag := f.Tag.Get(_ignoreUnexportedTag)
if tag == "" {
return false, nil
}
allowed, err := strconv.ParseBool(tag)
if err != nil {
err = newErrInvalidInput(
fmt.Sprintf("invalid value %q for %q tag on field %v", tag, _ignoreUnexportedTag, f.Name), err)
}
return allowed, err
}

665
vendor/go.uber.org/dig/provide.go generated vendored Normal file
View File

@@ -0,0 +1,665 @@
// 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
}

535
vendor/go.uber.org/dig/result.go generated vendored Normal file
View File

@@ -0,0 +1,535 @@
// Copyright (c) 2019-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"
"reflect"
"go.uber.org/dig/internal/digerror"
"go.uber.org/dig/internal/dot"
)
// The result interface represents a result produced by a constructor.
//
// The following implementations exist:
// resultList All values returned by the constructor.
// resultSingle A single value produced by a constructor.
// resultObject dig.Out struct where each field in the struct can be
// another result.
// resultGrouped A value produced by a constructor that is part of a value
// group.
type result interface {
// Extracts the values for this result from the provided value and
// stores them into the provided containerWriter.
//
// This MAY panic if the result does not consume a single value.
Extract(containerWriter, bool, reflect.Value)
// DotResult returns a slice of dot.Result(s).
DotResult() []*dot.Result
}
var (
_ result = resultSingle{}
_ result = resultObject{}
_ result = resultList{}
_ result = resultGrouped{}
)
type resultOptions struct {
// If set, this is the name of the associated result value.
//
// For Result Objects, name:".." tags on fields override this.
Name string
Group string
As []interface{}
}
// newResult builds a result from the given type.
func newResult(t reflect.Type, opts resultOptions) (result, error) {
switch {
case IsIn(t) || (t.Kind() == reflect.Ptr && IsIn(t.Elem())) || embedsType(t, _inPtrType):
return nil, newErrInvalidInput(fmt.Sprintf(
"cannot provide parameter objects: %v embeds a dig.In", t), nil)
case isError(t):
return nil, newErrInvalidInput("cannot return an error here, return it from the constructor instead", nil)
case IsOut(t):
return newResultObject(t, opts)
case embedsType(t, _outPtrType):
return nil, newErrInvalidInput(fmt.Sprintf(
"cannot build a result object by embedding *dig.Out, embed dig.Out instead: %v embeds *dig.Out", t), nil)
case t.Kind() == reflect.Ptr && IsOut(t.Elem()):
return nil, newErrInvalidInput(fmt.Sprintf(
"cannot return a pointer to a result object, use a value instead: %v is a pointer to a struct that embeds dig.Out", t), nil)
case len(opts.Group) > 0:
g, err := parseGroupString(opts.Group)
if err != nil {
return nil, newErrInvalidInput(
fmt.Sprintf("cannot parse group %q", opts.Group), err)
}
rg := resultGrouped{Type: t, Group: g.Name, Flatten: g.Flatten}
if len(opts.As) > 0 {
var asTypes []reflect.Type
for _, as := range opts.As {
ifaceType := reflect.TypeOf(as).Elem()
if ifaceType == t {
continue
}
if !t.Implements(ifaceType) {
return nil, newErrInvalidInput(
fmt.Sprintf("invalid dig.As: %v does not implement %v", t, ifaceType), nil)
}
asTypes = append(asTypes, ifaceType)
}
if len(asTypes) > 0 {
rg.Type = asTypes[0]
rg.As = asTypes[1:]
}
}
if g.Soft {
return nil, newErrInvalidInput(fmt.Sprintf(
"cannot use soft with result value groups: soft was used with group:%q", g.Name), nil)
}
if g.Flatten {
if t.Kind() != reflect.Slice {
return nil, newErrInvalidInput(fmt.Sprintf(
"flatten can be applied to slices only: %v is not a slice", t), nil)
}
rg.Type = rg.Type.Elem()
}
return rg, nil
default:
return newResultSingle(t, opts)
}
}
// resultVisitor visits every result in a result tree, allowing tracking state
// at each level.
type resultVisitor interface {
// Visit is called on the result being visited.
//
// If Visit returns a non-nil resultVisitor, that resultVisitor visits all
// the child results of this result.
Visit(result) resultVisitor
// AnnotateWithField is called on each field of a resultObject after
// visiting it but before walking its descendants.
//
// The same resultVisitor is used for all fields: the one returned upon
// visiting the resultObject.
//
// For each visited field, if AnnotateWithField returns a non-nil
// resultVisitor, it will be used to walk the result of that field.
AnnotateWithField(resultObjectField) resultVisitor
// AnnotateWithPosition is called with the index of each result of a
// resultList after vising it but before walking its descendants.
//
// The same resultVisitor is used for all results: the one returned upon
// visiting the resultList.
//
// For each position, if AnnotateWithPosition returns a non-nil
// resultVisitor, it will be used to walk the result at that index.
AnnotateWithPosition(idx int) resultVisitor
}
// walkResult walks the result tree for the given result with the provided
// visitor.
//
// resultVisitor.Visit will be called on the provided result and if a non-nil
// resultVisitor is received, it will be used to walk its descendants. If a
// resultObject or resultList was visited, AnnotateWithField and
// AnnotateWithPosition respectively will be called before visiting the
// descendants of that resultObject/resultList.
//
// This is very similar to how go/ast.Walk works.
func walkResult(r result, v resultVisitor) {
v = v.Visit(r)
if v == nil {
return
}
switch res := r.(type) {
case resultSingle, resultGrouped:
// No sub-results
case resultObject:
w := v
for _, f := range res.Fields {
if v := w.AnnotateWithField(f); v != nil {
walkResult(f.Result, v)
}
}
case resultList:
w := v
for i, r := range res.Results {
if v := w.AnnotateWithPosition(i); v != nil {
walkResult(r, v)
}
}
default:
digerror.BugPanicf("received unknown result type %T", res)
}
}
// resultList holds all values returned by the constructor as results.
type resultList struct {
ctype reflect.Type
Results []result
// For each item at index i returned by the constructor, resultIndexes[i]
// is the index in .Results for the corresponding result object.
// resultIndexes[i] is -1 for errors returned by constructors.
resultIndexes []int
}
func (rl resultList) DotResult() []*dot.Result {
var types []*dot.Result
for _, result := range rl.Results {
types = append(types, result.DotResult()...)
}
return types
}
func newResultList(ctype reflect.Type, opts resultOptions) (resultList, error) {
numOut := ctype.NumOut()
rl := resultList{
ctype: ctype,
Results: make([]result, 0, numOut),
resultIndexes: make([]int, numOut),
}
resultIdx := 0
for i := 0; i < numOut; i++ {
t := ctype.Out(i)
if isError(t) {
rl.resultIndexes[i] = -1
continue
}
r, err := newResult(t, opts)
if err != nil {
return rl, newErrInvalidInput(fmt.Sprintf("bad result %d", i+1), err)
}
rl.Results = append(rl.Results, r)
rl.resultIndexes[i] = resultIdx
resultIdx++
}
return rl, nil
}
func (resultList) Extract(containerWriter, bool, reflect.Value) {
digerror.BugPanicf("resultList.Extract() must never be called")
}
func (rl resultList) ExtractList(cw containerWriter, decorated bool, values []reflect.Value) error {
for i, v := range values {
if resultIdx := rl.resultIndexes[i]; resultIdx >= 0 {
rl.Results[resultIdx].Extract(cw, decorated, v)
continue
}
if err, _ := v.Interface().(error); err != nil {
return err
}
}
return nil
}
// resultSingle is an explicit value produced by a constructor, optionally
// with a name.
//
// This object will be added to the graph as-is.
type resultSingle struct {
Name string
Type reflect.Type
// If specified, this is a list of types which the value will be made
// available as, in addition to its own type.
As []reflect.Type
}
func newResultSingle(t reflect.Type, opts resultOptions) (resultSingle, error) {
r := resultSingle{
Type: t,
Name: opts.Name,
}
var asTypes []reflect.Type
for _, as := range opts.As {
ifaceType := reflect.TypeOf(as).Elem()
if ifaceType == t {
// Special case:
// c.Provide(func() io.Reader, As(new(io.Reader)))
// Ignore instead of erroring out.
continue
}
if !t.Implements(ifaceType) {
return r, newErrInvalidInput(
fmt.Sprintf("invalid dig.As: %v does not implement %v", t, ifaceType), nil)
}
asTypes = append(asTypes, ifaceType)
}
if len(asTypes) == 0 {
return r, nil
}
return resultSingle{
Type: asTypes[0],
Name: opts.Name,
As: asTypes[1:],
}, nil
}
func (rs resultSingle) DotResult() []*dot.Result {
dotResults := make([]*dot.Result, 0, len(rs.As)+1)
dotResults = append(dotResults, &dot.Result{
Node: &dot.Node{
Type: rs.Type,
Name: rs.Name,
},
})
for _, asType := range rs.As {
dotResults = append(dotResults, &dot.Result{
Node: &dot.Node{Type: asType, Name: rs.Name},
})
}
return dotResults
}
func (rs resultSingle) Extract(cw containerWriter, decorated bool, v reflect.Value) {
if decorated {
cw.setDecoratedValue(rs.Name, rs.Type, v)
return
}
cw.setValue(rs.Name, rs.Type, v)
for _, asType := range rs.As {
cw.setValue(rs.Name, asType, v)
}
}
// resultObject is a dig.Out struct where each field is another result.
//
// This object is not added to the graph. Its fields are interpreted as
// results and added to the graph if needed.
type resultObject struct {
Type reflect.Type
Fields []resultObjectField
}
func (ro resultObject) DotResult() []*dot.Result {
var types []*dot.Result
for _, field := range ro.Fields {
types = append(types, field.DotResult()...)
}
return types
}
func newResultObject(t reflect.Type, opts resultOptions) (resultObject, error) {
ro := resultObject{Type: t}
if len(opts.Name) > 0 {
return ro, newErrInvalidInput(fmt.Sprintf(
"cannot specify a name for result objects: %v embeds dig.Out", t), nil)
}
if len(opts.Group) > 0 {
return ro, newErrInvalidInput(fmt.Sprintf(
"cannot specify a group for result objects: %v embeds dig.Out", t), nil)
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Type == _outType {
// Skip over the dig.Out embed.
continue
}
rof, err := newResultObjectField(i, f, opts)
if err != nil {
return ro, newErrInvalidInput(fmt.Sprintf("bad field %q of %v", f.Name, t), err)
}
ro.Fields = append(ro.Fields, rof)
}
return ro, nil
}
func (ro resultObject) Extract(cw containerWriter, decorated bool, v reflect.Value) {
for _, f := range ro.Fields {
f.Result.Extract(cw, decorated, v.Field(f.FieldIndex))
}
}
// resultObjectField is a single field inside a dig.Out struct.
type resultObjectField struct {
// Name of the field in the struct.
FieldName string
// Index of the field in the struct.
//
// We need to track this separately because not all fields of the struct
// map to results.
FieldIndex int
// Result produced by this field.
Result result
}
func (rof resultObjectField) DotResult() []*dot.Result {
return rof.Result.DotResult()
}
// newResultObjectField(i, f, opts) builds a resultObjectField from the field
// f at index i.
func newResultObjectField(idx int, f reflect.StructField, opts resultOptions) (resultObjectField, error) {
rof := resultObjectField{
FieldName: f.Name,
FieldIndex: idx,
}
var r result
switch {
case f.PkgPath != "":
return rof, newErrInvalidInput(
fmt.Sprintf("unexported fields not allowed in dig.Out, did you mean to export %q (%v)?", f.Name, f.Type), nil)
case f.Tag.Get(_groupTag) != "":
var err error
r, err = newResultGrouped(f)
if err != nil {
return rof, err
}
default:
var err error
if name := f.Tag.Get(_nameTag); len(name) > 0 {
// can modify in-place because options are passed-by-value.
opts.Name = name
}
r, err = newResult(f.Type, opts)
if err != nil {
return rof, err
}
}
rof.Result = r
return rof, nil
}
// resultGrouped is a value produced by a constructor that is part of a result
// group.
//
// These will be produced as fields of a dig.Out struct.
type resultGrouped struct {
// Name of the group as specified in the `group:".."` tag.
Group string
// Type of value produced.
Type reflect.Type
// Indicates elements of a value are to be injected individually, instead of
// as a group. Requires the value's slice to be a group. If set, Type will be
// the type of individual elements rather than the group.
Flatten bool
// If specified, this is a list of types which the value will be made
// available as, in addition to its own type.
As []reflect.Type
}
func (rt resultGrouped) DotResult() []*dot.Result {
dotResults := make([]*dot.Result, 0, len(rt.As)+1)
dotResults = append(dotResults, &dot.Result{
Node: &dot.Node{
Type: rt.Type,
Group: rt.Group,
},
})
for _, asType := range rt.As {
dotResults = append(dotResults, &dot.Result{
Node: &dot.Node{Type: asType, Group: rt.Group},
})
}
return dotResults
}
// newResultGrouped(f) builds a new resultGrouped from the provided field.
func newResultGrouped(f reflect.StructField) (resultGrouped, error) {
g, err := parseGroupString(f.Tag.Get(_groupTag))
if err != nil {
return resultGrouped{}, err
}
rg := resultGrouped{
Group: g.Name,
Flatten: g.Flatten,
Type: f.Type,
}
name := f.Tag.Get(_nameTag)
optional, _ := isFieldOptional(f)
switch {
case g.Flatten && f.Type.Kind() != reflect.Slice:
return rg, newErrInvalidInput(fmt.Sprintf(
"flatten can be applied to slices only: field %q (%v) is not a slice", f.Name, f.Type), nil)
case g.Soft:
return rg, newErrInvalidInput(fmt.Sprintf(
"cannot use soft with result value groups: soft was used with group %q", rg.Group), nil)
case name != "":
return rg, newErrInvalidInput(fmt.Sprintf(
"cannot use named values with value groups: name:%q provided with group:%q", name, rg.Group), nil)
case optional:
return rg, newErrInvalidInput("value groups cannot be optional", nil)
}
if g.Flatten {
rg.Type = f.Type.Elem()
}
return rg, nil
}
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)
for _, asType := range rt.As {
cw.submitGroupedValue(rt.Group, asType, v)
}
return
}
if decorated {
cw.submitDecoratedGroupedValue(rt.Group, rt.Type, v)
return
}
for i := 0; i < v.Len(); i++ {
cw.submitGroupedValue(rt.Group, rt.Type, v.Index(i))
}
}

326
vendor/go.uber.org/dig/scope.go generated vendored Normal file
View File

@@ -0,0 +1,326 @@
// 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"
"math/rand"
"reflect"
"sort"
"time"
)
// A ScopeOption modifies the default behavior of Scope; currently,
// there are no implementations.
type ScopeOption interface {
noScopeOption() //yet
}
// Scope is a scoped DAG of types and their dependencies.
// A Scope may also have one or more child Scopes that inherit
// from it.
type Scope struct {
// This implements containerStore interface.
// Name of the Scope
name string
// Mapping from key to all the constructor node that can provide a value for that
// key.
providers map[key][]*constructorNode
// Mapping from key to the decorator that decorates a value for that key.
decorators map[key]*decoratorNode
// constructorNodes provided directly to this Scope. i.e. it does not include
// any nodes that were provided to the parent Scope this inherited from.
nodes []*constructorNode
// Values that generated via decorators in the Scope.
decoratedValues map[key]reflect.Value
// Values that generated directly in the Scope.
values map[key]reflect.Value
// Values groups that generated directly in the Scope.
groups map[key][]reflect.Value
// Values groups that generated via decoraters in the Scope.
decoratedGroups map[key]reflect.Value
// Source of randomness.
rand *rand.Rand
// Flag indicating whether the graph has been checked for cycles.
isVerifiedAcyclic bool
// Defer acyclic check on provide until Invoke.
deferAcyclicVerification bool
// Recover from panics in user-provided code and wrap in an exported error type.
recoverFromPanics bool
// invokerFn calls a function with arguments provided to Provide or Invoke.
invokerFn invokerFn
// graph of this Scope. Note that this holds the dependency graph of all the
// nodes that affect this Scope, not just the ones provided directly to this Scope.
gh *graphHolder
// Parent of this Scope.
parentScope *Scope
// All the child scopes of this Scope.
childScopes []*Scope
}
func newScope() *Scope {
s := &Scope{
providers: make(map[key][]*constructorNode),
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),
invokerFn: defaultInvoker,
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
}
s.gh = newGraphHolder(s)
return s
}
// Scope creates a new Scope with the given name and options from current Scope.
// Any constructors that the current Scope knows about, as well as any modifications
// made to it in the future will be propagated to the child scope.
// However, no modifications made to the child scope being created will be propagated
// to the parent Scope.
func (s *Scope) Scope(name string, opts ...ScopeOption) *Scope {
child := newScope()
child.name = name
child.parentScope = s
child.invokerFn = s.invokerFn
child.deferAcyclicVerification = s.deferAcyclicVerification
child.recoverFromPanics = s.recoverFromPanics
// child copies the parent's graph nodes.
for _, node := range s.gh.nodes {
child.gh.nodes = append(child.gh.nodes, node)
if ctrNode, ok := node.Wrapped.(*constructorNode); ok {
ctrNode.CopyOrder(s, child)
}
}
for _, opt := range opts {
opt.noScopeOption()
}
s.childScopes = append(s.childScopes, child)
return child
}
// ancestors returns a list of scopes of ancestors of this scope up to the
// root. The scope at at index 0 is this scope itself.
func (s *Scope) ancestors() []*Scope {
var scopes []*Scope
for s := s; s != nil; s = s.parentScope {
scopes = append(scopes, s)
}
return scopes
}
func (s *Scope) appendSubscopes(dest []*Scope) []*Scope {
dest = append(dest, s)
for _, cs := range s.childScopes {
dest = cs.appendSubscopes(dest)
}
return dest
}
func (s *Scope) storesToRoot() []containerStore {
scopes := s.ancestors()
stores := make([]containerStore, len(scopes))
for i, s := range scopes {
stores[i] = s
}
return stores
}
func (s *Scope) knownTypes() []reflect.Type {
typeSet := make(map[reflect.Type]struct{}, len(s.providers))
for k := range s.providers {
typeSet[k.t] = struct{}{}
}
types := make([]reflect.Type, 0, len(typeSet))
for t := range typeSet {
types = append(types, t)
}
sort.Sort(byTypeName(types))
return types
}
func (s *Scope) getValue(name string, t reflect.Type) (v reflect.Value, ok bool) {
v, ok = s.values[key{name: name, t: t}]
return
}
func (s *Scope) getDecoratedValue(name string, t reflect.Type) (v reflect.Value, ok bool) {
v, ok = s.decoratedValues[key{name: name, t: t}]
return
}
func (s *Scope) setValue(name string, t reflect.Type, v reflect.Value) {
s.values[key{name: name, t: t}] = v
}
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 {
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
}
func (s *Scope) submitGroupedValue(name string, t reflect.Type, v reflect.Value) {
k := key{group: name, t: t}
s.groups[k] = append(s.groups[k], v)
}
func (s *Scope) submitDecoratedGroupedValue(name string, t reflect.Type, v reflect.Value) {
k := key{group: name, t: t}
s.decoratedGroups[k] = v
}
func (s *Scope) getValueProviders(name string, t reflect.Type) []provider {
return s.getProviders(key{name: name, t: t})
}
func (s *Scope) getGroupProviders(name string, t reflect.Type) []provider {
return s.getProviders(key{group: name, t: t})
}
func (s *Scope) getValueDecorator(name string, t reflect.Type) (decorator, bool) {
return s.getDecorators(key{name: name, t: t})
}
func (s *Scope) getGroupDecorator(name string, t reflect.Type) (decorator, bool) {
return s.getDecorators(key{group: name, t: t})
}
func (s *Scope) getDecorators(k key) (decorator, bool) {
d, found := s.decorators[k]
return d, found
}
func (s *Scope) getProviders(k key) []provider {
nodes := s.providers[k]
providers := make([]provider, len(nodes))
for i, n := range nodes {
providers[i] = n
}
return providers
}
func (s *Scope) getAllGroupProviders(name string, t reflect.Type) []provider {
return s.getAllProviders(key{group: name, t: t})
}
func (s *Scope) getAllValueProviders(name string, t reflect.Type) []provider {
return s.getAllProviders(key{name: name, t: t})
}
func (s *Scope) getAllProviders(k key) []provider {
allScopes := s.ancestors()
var providers []provider
for _, scope := range allScopes {
providers = append(providers, scope.getProviders(k)...)
}
return providers
}
func (s *Scope) invoker() invokerFn {
return s.invokerFn
}
// adds a new graphNode to this Scope and all of its descendent
// scope.
func (s *Scope) newGraphNode(wrapped interface{}, orders map[*Scope]int) {
orders[s] = s.gh.NewNode(wrapped)
for _, cs := range s.childScopes {
cs.newGraphNode(wrapped, orders)
}
}
func (s *Scope) cycleDetectedError(cycle []int) error {
var path []cycleErrPathEntry
for _, n := range cycle {
if n, ok := s.gh.Lookup(n).(*constructorNode); ok {
path = append(path, cycleErrPathEntry{
Key: key{
t: n.CType(),
},
Func: n.Location(),
})
}
}
return errCycleDetected{Path: path, scope: s}
}
// Returns the root Scope that can be reached from this Scope.
func (s *Scope) rootScope() *Scope {
curr := s
for curr.parentScope != nil {
curr = curr.parentScope
}
return curr
}
// String representation of the entire Scope
func (s *Scope) String() string {
b := &bytes.Buffer{}
fmt.Fprintln(b, "nodes: {")
for k, vs := range s.providers {
for _, v := range vs {
fmt.Fprintln(b, "\t", k, "->", v)
}
}
fmt.Fprintln(b, "}")
fmt.Fprintln(b, "values: {")
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)
}
}
fmt.Fprintln(b, "}")
return b.String()
}

24
vendor/go.uber.org/dig/version.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
// Copyright (c) 2019 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
// Version of the library.
const Version = "1.17.1"

192
vendor/go.uber.org/dig/visualize.go generated vendored Normal file
View File

@@ -0,0 +1,192 @@
// 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 (
"errors"
"fmt"
"io"
"strconv"
"text/template"
"go.uber.org/dig/internal/dot"
)
// A VisualizeOption modifies the default behavior of Visualize.
type VisualizeOption interface {
applyVisualizeOption(*visualizeOptions)
}
type visualizeOptions struct {
VisualizeError error
}
// VisualizeError includes a visualization of the given error in the output of
// Visualize if an error was returned by Invoke or Provide.
//
// if err := c.Provide(...); err != nil {
// dig.Visualize(c, w, dig.VisualizeError(err))
// }
//
// This option has no effect if the error was nil or if it didn't contain any
// information to visualize.
func VisualizeError(err error) VisualizeOption {
return visualizeErrorOption{err}
}
type visualizeErrorOption struct{ err error }
func (o visualizeErrorOption) String() string {
return fmt.Sprintf("VisualizeError(%v)", o.err)
}
func (o visualizeErrorOption) applyVisualizeOption(opt *visualizeOptions) {
opt.VisualizeError = o.err
}
func updateGraph(dg *dot.Graph, err error) error {
var errs []errVisualizer
// Unwrap error to find the root cause.
for {
if ev, ok := err.(errVisualizer); ok {
errs = append(errs, ev)
}
e := errors.Unwrap(err)
if e == nil {
break
}
err = e
}
// If there are no errVisualizers included, we do not modify the graph.
if len(errs) == 0 {
return nil
}
// We iterate in reverse because the last element is the root cause.
for i := len(errs) - 1; i >= 0; i-- {
errs[i].updateGraph(dg)
}
// Remove non-error entries from the graph for readability.
dg.PruneSuccess()
return nil
}
var _graphTmpl = template.Must(
template.New("DotGraph").
Funcs(template.FuncMap{
"quote": strconv.Quote,
}).
Parse(`digraph {
rankdir=RL;
graph [compound=true];
{{range $g := .Groups}}
{{- quote .String}} [{{.Attributes}}];
{{range .Results}}
{{- quote $g.String}} -> {{quote .String}};
{{end}}
{{end -}}
{{range $index, $ctor := .Ctors}}
subgraph cluster_{{$index}} {
{{ with .Package }}label = {{ quote .}};
{{ end -}}
constructor_{{$index}} [shape=plaintext label={{quote .Name}}];
{{with .ErrorType}}color={{.Color}};{{end}}
{{range .Results}}
{{- quote .String}} [{{.Attributes}}];
{{end}}
}
{{range .Params}}
constructor_{{$index}} -> {{quote .String}} [ltail=cluster_{{$index}}{{if .Optional}} style=dashed{{end}}];
{{end}}
{{range .GroupParams}}
constructor_{{$index}} -> {{quote .String}} [ltail=cluster_{{$index}}];
{{end -}}
{{end}}
{{range .Failed.TransitiveFailures}}
{{- quote .String}} [color=orange];
{{end -}}
{{range .Failed.RootCauses}}
{{- quote .String}} [color=red];
{{end}}
}`))
// Visualize parses the graph in Container c into DOT format and writes it to
// io.Writer w.
func Visualize(c *Container, w io.Writer, opts ...VisualizeOption) error {
dg := c.createGraph()
var options visualizeOptions
for _, o := range opts {
o.applyVisualizeOption(&options)
}
if options.VisualizeError != nil {
if err := updateGraph(dg, options.VisualizeError); err != nil {
return err
}
}
return _graphTmpl.Execute(w, dg)
}
// CanVisualizeError returns true if the error is an errVisualizer.
func CanVisualizeError(err error) bool {
for {
if _, ok := err.(errVisualizer); ok {
return true
}
e := errors.Unwrap(err)
if e == nil {
break
}
err = e
}
return false
}
func (c *Container) createGraph() *dot.Graph {
return c.scope.createGraph()
}
func (s *Scope) createGraph() *dot.Graph {
dg := dot.NewGraph()
for _, n := range s.nodes {
dg.AddCtor(newDotCtor(n), n.paramList.DotParam(), n.resultList.DotResult())
}
return dg
}
func newDotCtor(n *constructorNode) *dot.Ctor {
return &dot.Ctor{
ID: n.id,
Name: n.location.Name,
Package: n.location.Package,
File: n.location.File,
Line: n.location.Line,
}
}