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:
19
vendor/go.uber.org/dig/.codecov.yml
generated
vendored
Normal file
19
vendor/go.uber.org/dig/.codecov.yml
generated
vendored
Normal 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
12
vendor/go.uber.org/dig/.gitignore
generated
vendored
Normal 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
309
vendor/go.uber.org/dig/CHANGELOG.md
generated
vendored
Normal 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
19
vendor/go.uber.org/dig/LICENSE
generated
vendored
Normal 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
67
vendor/go.uber.org/dig/Makefile
generated
vendored
Normal 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
51
vendor/go.uber.org/dig/README.md
generated
vendored
Normal 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
|
||||
|
||||
[](https://starchart.cc/uber-go/dig)
|
||||
|
||||
108
vendor/go.uber.org/dig/callback.go
generated
vendored
Normal file
108
vendor/go.uber.org/dig/callback.go
generated
vendored
Normal 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
17
vendor/go.uber.org/dig/check_license.sh
generated
vendored
Normal 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
243
vendor/go.uber.org/dig/constructor.go
generated
vendored
Normal 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
282
vendor/go.uber.org/dig/container.go
generated
vendored
Normal 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
79
vendor/go.uber.org/dig/cycle_error.go
generated
vendored
Normal 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
313
vendor/go.uber.org/dig/decorate.go
generated
vendored
Normal 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
348
vendor/go.uber.org/dig/doc.go
generated
vendored
Normal 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
527
vendor/go.uber.org/dig/error.go
generated
vendored
Normal 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
7
vendor/go.uber.org/dig/glide.yaml
generated
vendored
Normal 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
115
vendor/go.uber.org/dig/graph.go
generated
vendored
Normal 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
67
vendor/go.uber.org/dig/group.go
generated
vendored
Normal 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
175
vendor/go.uber.org/dig/inout.go
generated
vendored
Normal 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
34
vendor/go.uber.org/dig/internal/digerror/errors.go
generated
vendored
Normal 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
125
vendor/go.uber.org/dig/internal/digreflect/func.go
generated
vendored
Normal 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
61
vendor/go.uber.org/dig/internal/dot/README.md
generated
vendored
Normal 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
466
vendor/go.uber.org/dig/internal/dot/graph.go
generated
vendored
Normal 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
118
vendor/go.uber.org/dig/internal/graph/graph.go
generated
vendored
Normal 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
211
vendor/go.uber.org/dig/invoke.go
generated
vendored
Normal 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
668
vendor/go.uber.org/dig/param.go
generated
vendored
Normal 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
665
vendor/go.uber.org/dig/provide.go
generated
vendored
Normal 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
535
vendor/go.uber.org/dig/result.go
generated
vendored
Normal 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
326
vendor/go.uber.org/dig/scope.go
generated
vendored
Normal 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
24
vendor/go.uber.org/dig/version.go
generated
vendored
Normal 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
192
vendor/go.uber.org/dig/visualize.go
generated
vendored
Normal 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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user