mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Vendor wire into pkg/build (#84637)
* vendor latest wire into pkg/build * use vendored wire in builds * fix wire import path * remove wire from bingo * also support google/wire import * make prettier happy * change package in tess * add debug walk for drone * add wire_gen in tests * remove debug walk * restore imports
This commit is contained in:
parent
36ee1571b6
commit
4d4c06b480
@ -1,4 +1,4 @@
|
|||||||
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT.
|
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT.
|
||||||
# All tools are designed to be build inside $GOBIN.
|
# All tools are designed to be build inside $GOBIN.
|
||||||
BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
|
BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||||
GOPATH ?= $(shell go env GOPATH)
|
GOPATH ?= $(shell go env GOPATH)
|
||||||
@ -59,9 +59,3 @@ $(SWAGGER): $(BINGO_DIR)/swagger.mod
|
|||||||
@echo "(re)installing $(GOBIN)/swagger-v0.30.2"
|
@echo "(re)installing $(GOBIN)/swagger-v0.30.2"
|
||||||
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=swagger.mod -o=$(GOBIN)/swagger-v0.30.2 "github.com/go-swagger/go-swagger/cmd/swagger"
|
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=swagger.mod -o=$(GOBIN)/swagger-v0.30.2 "github.com/go-swagger/go-swagger/cmd/swagger"
|
||||||
|
|
||||||
WIRE := $(GOBIN)/wire-v0.6.0
|
|
||||||
$(WIRE): $(BINGO_DIR)/wire.mod
|
|
||||||
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
|
|
||||||
@echo "(re)installing $(GOBIN)/wire-v0.6.0"
|
|
||||||
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=wire.mod -o=$(GOBIN)/wire-v0.6.0 "github.com/google/wire/cmd/wire"
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT.
|
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT.
|
||||||
# All tools are designed to be build inside $GOBIN.
|
# All tools are designed to be build inside $GOBIN.
|
||||||
# Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk.
|
# Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk.
|
||||||
GOBIN=${GOBIN:=$(go env GOBIN)}
|
GOBIN=${GOBIN:=$(go env GOBIN)}
|
||||||
@ -22,5 +22,3 @@ LEFTHOOK="${GOBIN}/lefthook-v1.4.8"
|
|||||||
|
|
||||||
SWAGGER="${GOBIN}/swagger-v0.30.2"
|
SWAGGER="${GOBIN}/swagger-v0.30.2"
|
||||||
|
|
||||||
WIRE="${GOBIN}/wire-v0.6.0"
|
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT
|
|
||||||
|
|
||||||
go 1.16
|
|
||||||
|
|
||||||
require github.com/google/wire v0.6.0 // cmd/wire
|
|
4
Makefile
4
Makefile
@ -117,9 +117,9 @@ gen-feature-toggles:
|
|||||||
go test -v ./pkg/services/featuremgmt/...; \
|
go test -v ./pkg/services/featuremgmt/...; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
gen-go: $(WIRE)
|
gen-go:
|
||||||
@echo "generate go files"
|
@echo "generate go files"
|
||||||
$(WIRE) gen -tags $(WIRE_TAGS) ./pkg/server
|
$(GO) run ./pkg/build/wire/cmd/wire/main.go gen -tags $(WIRE_TAGS) ./pkg/server
|
||||||
|
|
||||||
fix-cue: $(CUE)
|
fix-cue: $(CUE)
|
||||||
@echo "formatting cue files"
|
@echo "formatting cue files"
|
||||||
|
1
go.work
1
go.work
@ -4,6 +4,7 @@ use (
|
|||||||
.
|
.
|
||||||
./pkg/apimachinery
|
./pkg/apimachinery
|
||||||
./pkg/apiserver
|
./pkg/apiserver
|
||||||
|
./pkg/build/wire
|
||||||
./pkg/promlib
|
./pkg/promlib
|
||||||
./pkg/util/xorm
|
./pkg/util/xorm
|
||||||
)
|
)
|
||||||
|
18
pkg/build/wire/AUTHORS
Normal file
18
pkg/build/wire/AUTHORS
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# This is the official list of Wire authors for copyright purposes.
|
||||||
|
# This file is distinct from the CONTRIBUTORS files.
|
||||||
|
# See the latter for an explanation.
|
||||||
|
|
||||||
|
# Names should be added to this file as one of
|
||||||
|
# Organization's name
|
||||||
|
# Individual's name <submission email address>
|
||||||
|
# Individual's name <submission email address> <email2> <emailN>
|
||||||
|
# See CONTRIBUTORS for the meaning of multiple email addresses.
|
||||||
|
|
||||||
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Google LLC
|
||||||
|
ktr <ktr@syfm.me>
|
||||||
|
Kumbirai Tanekha <kumbirai.tanekha@gmail.com>
|
||||||
|
Oleg Kovalov <iamolegkovalov@gmail.com>
|
||||||
|
Yoichiro Shimizu <budougumi0617@gmail.com>
|
||||||
|
Zachary Romero <zacromero3@gmail.com>
|
10
pkg/build/wire/CODE_OF_CONDUCT.md
Normal file
10
pkg/build/wire/CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Code of Conduct
|
||||||
|
|
||||||
|
This project is covered under the [Go Code of Conduct][]. In summary:
|
||||||
|
|
||||||
|
- Treat everyone with respect and kindness.
|
||||||
|
- Be thoughtful in how you communicate.
|
||||||
|
- Don’t be destructive or inflammatory.
|
||||||
|
- If you encounter an issue, please mail conduct@golang.org.
|
||||||
|
|
||||||
|
[Go Code of Conduct]: https://golang.org/conduct
|
152
pkg/build/wire/CONTRIBUTING.md
Normal file
152
pkg/build/wire/CONTRIBUTING.md
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
# How to Contribute
|
||||||
|
|
||||||
|
We would love to accept your patches and contributions to this project. Here is
|
||||||
|
how you can help.
|
||||||
|
|
||||||
|
## Filing issues
|
||||||
|
|
||||||
|
Filing issues is an important way you can contribute to the Wire Project. We
|
||||||
|
want your feedback on things like bugs, desired API changes, or just anything
|
||||||
|
that isn't working for you.
|
||||||
|
|
||||||
|
### Bugs
|
||||||
|
|
||||||
|
If your issue is a bug, open one
|
||||||
|
[here](https://github.com/google/wire/issues/new). The easiest way to file an
|
||||||
|
issue with all the right information is to run `go bug`. `go bug` will print out
|
||||||
|
a handy template of questions and system information that will help us get to
|
||||||
|
the root of the issue quicker.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
Unlike the core Go project, we do not have a formal proposal process for
|
||||||
|
changes. If you have a change you would like to see in Wire, please file an
|
||||||
|
issue with the necessary details.
|
||||||
|
|
||||||
|
### Triaging
|
||||||
|
|
||||||
|
The Go Cloud team triages issues at least every two weeks, but usually within
|
||||||
|
two business days. Bugs or feature requests are either placed into a **Sprint**
|
||||||
|
milestone which means the issue is intended to be worked on. Issues that we
|
||||||
|
would like to address but do not have time for are placed into the [Unplanned][]
|
||||||
|
milestone.
|
||||||
|
|
||||||
|
[Unplanned]: https://github.com/google/wire/milestone/1
|
||||||
|
|
||||||
|
## Contributing Code
|
||||||
|
|
||||||
|
We love accepting contributions! If your change is minor, please feel free
|
||||||
|
submit a [pull request](https://help.github.com/articles/about-pull-requests/).
|
||||||
|
If your change is larger, or adds a feature, please file an issue beforehand so
|
||||||
|
that we can discuss the change. You're welcome to file an implementation pull
|
||||||
|
request immediately as well, although we generally lean towards discussing the
|
||||||
|
change and then reviewing the implementation separately.
|
||||||
|
|
||||||
|
### Finding something to work on
|
||||||
|
|
||||||
|
If you want to write some code, but don't know where to start or what you might
|
||||||
|
want to do, take a look at our [Unplanned][] milestone. This is where you can
|
||||||
|
find issues we would like to address but can't currently find time for. See if
|
||||||
|
any of the latest ones look interesting! If you need help before you can start
|
||||||
|
work, you can comment on the issue and we will try to help as best we can.
|
||||||
|
|
||||||
|
### Contributor License Agreement
|
||||||
|
|
||||||
|
Contributions to this project can only be made by those who have signed Google's
|
||||||
|
Contributor License Agreement. You (or your employer) retain the copyright to
|
||||||
|
your contribution, this simply gives us permission to use and redistribute your
|
||||||
|
contributions as part of the project. Head over to
|
||||||
|
<https://cla.developers.google.com/> to see your current agreements on file or
|
||||||
|
to sign a new one.
|
||||||
|
|
||||||
|
As a personal contributor, you only need to sign the Google CLA once across all
|
||||||
|
Google projects. If you've already signed the CLA, there is no need to do it
|
||||||
|
again. If you are submitting code on behalf of your employer, there's
|
||||||
|
[a separate corporate CLA that your employer manages for you](https://opensource.google.com/docs/cla/#external-contributors).
|
||||||
|
|
||||||
|
## Making a pull request
|
||||||
|
|
||||||
|
* Follow the normal
|
||||||
|
[pull request flow](https://help.github.com/articles/creating-a-pull-request/)
|
||||||
|
* Build your changes using Go 1.11 with Go modules enabled. Wire's continuous
|
||||||
|
integration uses Go modules in order to ensure
|
||||||
|
[reproducible builds](https://research.swtch.com/vgo-repro).
|
||||||
|
* Test your changes using `go test ./...`. Please add tests that show the
|
||||||
|
change does what it says it does, even if there wasn't a test in the first
|
||||||
|
place.
|
||||||
|
* Feel free to make as many commits as you want; we will squash them all into
|
||||||
|
a single commit before merging your change.
|
||||||
|
* Check the diffs, write a useful description (including something like
|
||||||
|
`Fixes #123` if it's fixing a bug) and send the PR out.
|
||||||
|
* Github will run tests against the PR. This should
|
||||||
|
happen within 10 minutes or so. If a test fails, go back to the coding stage
|
||||||
|
and try to fix the test and push the same branch again. You won't need to
|
||||||
|
make a new pull request, the changes will be rolled directly into the PR you
|
||||||
|
already opened. Wait for the tests again. There is no need to assign a reviewer
|
||||||
|
to the PR, the project team will assign someone for review during the
|
||||||
|
standard [triage](#triaging) process.
|
||||||
|
|
||||||
|
## Code review
|
||||||
|
|
||||||
|
All submissions, including submissions by project members, require review. It is
|
||||||
|
almost never the case that a pull request is accepted without some changes
|
||||||
|
requested, so please do not be offended!
|
||||||
|
|
||||||
|
When you have finished making requested changes to your pull request, please
|
||||||
|
make a comment containing "PTAL" (Please Take Another Look) on your pull
|
||||||
|
request. GitHub notifications can be noisy, and it is unfortunately easy for
|
||||||
|
things to be lost in the shuffle.
|
||||||
|
|
||||||
|
Once your PR is approved (hooray!) the reviewer will squash your commits into a
|
||||||
|
single commit, and then merge the commit onto the Wire master branch. Thank you!
|
||||||
|
|
||||||
|
## Github code review workflow conventions
|
||||||
|
|
||||||
|
(For project members and frequent contributors.)
|
||||||
|
|
||||||
|
As a contributor:
|
||||||
|
|
||||||
|
- Try hard to make each Pull Request as small and focused as possible. In
|
||||||
|
particular, this means that if a reviewer asks you to do something that is
|
||||||
|
beyond the scope of the Pull Request, the best practice is to file another
|
||||||
|
issue and reference it from the Pull Request rather than just adding more
|
||||||
|
commits to the existing PR.
|
||||||
|
- Adding someone as a Reviewer means "please feel free to look and comment";
|
||||||
|
the review is optional. Choose as many Reviewers as you'd like.
|
||||||
|
- Adding someone as an Assignee means that the Pull Request should not be
|
||||||
|
submitted until they approve. If you choose multiple Assignees, wait until
|
||||||
|
all of them approve. It is fine to ask someone if they are OK with being
|
||||||
|
removed as an Assignee.
|
||||||
|
- Note that if you don't select any assignees, ContributeBot will turn all
|
||||||
|
of your Reviewers into Assignees.
|
||||||
|
- Make as many commits as you want locally, but try not to push them to Github
|
||||||
|
until you've addressed comments; this allows the email notification about
|
||||||
|
the push to be a signal to reviewers that the PR is ready to be looked at
|
||||||
|
again.
|
||||||
|
- When there may be confusion about what should happen next for a PR, be
|
||||||
|
explicit; add a "PTAL" comment if it is ready for review again, or a "Please
|
||||||
|
hold off on reviewing for now" if you are still working on addressing
|
||||||
|
comments.
|
||||||
|
- "Resolve" comments that you are sure you've addressed; let your reviewers
|
||||||
|
resolve ones that you're not sure about.
|
||||||
|
- Do not use `git push --force`; this can cause comments from your reviewers
|
||||||
|
that are associated with a specific commit to be lost. This implies that
|
||||||
|
once you've sent a Pull Request, you should use `git merge` instead of `git
|
||||||
|
rebase` to incorporate commits from the master branch.
|
||||||
|
|
||||||
|
As a reviewer:
|
||||||
|
|
||||||
|
- Be timely in your review process, especially if you are an Assignee.
|
||||||
|
- Try to use `Start a Review` instead of single comments, to reduce email
|
||||||
|
spam.
|
||||||
|
- "Resolve" your own comments if they have been addressed.
|
||||||
|
- If you want your review to be blocking, and are not currently an Assignee,
|
||||||
|
add yourself as an Assignee.
|
||||||
|
|
||||||
|
When squashing-and-merging:
|
||||||
|
|
||||||
|
- Ensure that **all** of the Assignees have approved.
|
||||||
|
- Do a final review of the one-line PR summary, ensuring that it accurately
|
||||||
|
describes the change.
|
||||||
|
- Delete the automatically added commit lines; these are generally not
|
||||||
|
interesting and make commit history harder to read.
|
43
pkg/build/wire/CONTRIBUTORS
Normal file
43
pkg/build/wire/CONTRIBUTORS
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# This is the official list of people who can contribute
|
||||||
|
# (and typically have contributed) code to the Wire repository.
|
||||||
|
# The AUTHORS file lists the copyright holders; this file
|
||||||
|
# lists people. For example, Google employees are listed here
|
||||||
|
# but not in AUTHORS, because Google holds the copyright.
|
||||||
|
#
|
||||||
|
# Names should be added to this file only after verifying that
|
||||||
|
# the individual or the individual's organization has agreed to
|
||||||
|
# the appropriate Contributor License Agreement, found here:
|
||||||
|
#
|
||||||
|
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||||
|
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||||
|
#
|
||||||
|
# The agreement for individuals can be filled out on the web.
|
||||||
|
#
|
||||||
|
# When adding J Random Contributor's name to this file,
|
||||||
|
# either J's name or J's organization's name should be
|
||||||
|
# added to the AUTHORS file, depending on whether the
|
||||||
|
# individual or corporate CLA was used.
|
||||||
|
|
||||||
|
# Names should be added to this file like so:
|
||||||
|
# Individual's name <submission email address>
|
||||||
|
# Individual's name <submission email address> <email2> <emailN>
|
||||||
|
#
|
||||||
|
# An entry with multiple email addresses specifies that the
|
||||||
|
# first address should be used in the submit logs and
|
||||||
|
# that the other addresses should be recognized as the
|
||||||
|
# same person when interacting with Git.
|
||||||
|
|
||||||
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Chris Lewis <cflewis@google.com> <cflewis@golang.org> <c@chris.to>
|
||||||
|
Christina Austin <4240737+clausti@users.noreply.github.com>
|
||||||
|
Eno Compton <enocom@google.com>
|
||||||
|
Issac Trotts <issactrotts@google.com> <issac.trotts@gmail.com>
|
||||||
|
ktr <ktr@syfm.me>
|
||||||
|
Kumbirai Tanekha <kumbirai.tanekha@gmail.com>
|
||||||
|
Oleg Kovalov <iamolegkovalov@gmail.com>
|
||||||
|
Robert van Gent <rvangent@google.com> <vangent@gmail.com>
|
||||||
|
Ross Light <light@google.com> <ross@zombiezen.com>
|
||||||
|
Tuo Shan <shantuo@google.com> <sturbo89@gmail.com>
|
||||||
|
Yoichiro Shimizu <budougumi0617@gmail.com>
|
||||||
|
Zachary Romero <zacromero3@gmail.com>
|
202
pkg/build/wire/LICENSE
Normal file
202
pkg/build/wire/LICENSE
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
60
pkg/build/wire/README.md
Normal file
60
pkg/build/wire/README.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Wire: Automated Initialization in Go
|
||||||
|
|
||||||
|
[](https://github.com/google/wire/actions)
|
||||||
|
[][godoc]
|
||||||
|
[](https://codecov.io/gh/google/wire)
|
||||||
|
|
||||||
|
|
||||||
|
Wire is a code generation tool that automates connecting components using
|
||||||
|
[dependency injection][]. Dependencies between components are represented in
|
||||||
|
Wire as function parameters, encouraging explicit initialization instead of
|
||||||
|
global variables. Because Wire operates without runtime state or reflection,
|
||||||
|
code written to be used with Wire is useful even for hand-written
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
For an overview, see the [introductory blog post][].
|
||||||
|
|
||||||
|
[dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection
|
||||||
|
[introductory blog post]: https://blog.golang.org/wire
|
||||||
|
[godoc]: https://godoc.org/github.com/google/wire
|
||||||
|
[travis]: https://travis-ci.com/google/wire
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
Install Wire by running:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go install github.com/google/wire/cmd/wire@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
and ensuring that `$GOPATH/bin` is added to your `$PATH`.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [Tutorial][]
|
||||||
|
- [User Guide][]
|
||||||
|
- [Best Practices][]
|
||||||
|
- [FAQ][]
|
||||||
|
|
||||||
|
[Tutorial]: ./_tutorial/README.md
|
||||||
|
[Best Practices]: ./docs/best-practices.md
|
||||||
|
[FAQ]: ./docs/faq.md
|
||||||
|
[User Guide]: ./docs/guide.md
|
||||||
|
|
||||||
|
## Project status
|
||||||
|
|
||||||
|
As of version v0.3.0, Wire is *beta* and is considered feature complete. It
|
||||||
|
works well for the tasks it was designed to perform, and we prefer to keep it
|
||||||
|
as simple as possible.
|
||||||
|
|
||||||
|
We'll not be accepting new features at this time, but will gladly accept bug
|
||||||
|
reports and fixes.
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
For questions, please use [GitHub Discussions](https://github.com/google/wire/discussions).
|
||||||
|
|
||||||
|
This project is covered by the Go [Code of Conduct][].
|
||||||
|
|
||||||
|
[Code of Conduct]: ./CODE_OF_CONDUCT.md
|
||||||
|
[go-cloud mailing list]: https://groups.google.com/forum/#!forum/go-cloud
|
419
pkg/build/wire/_tutorial/README.md
Normal file
419
pkg/build/wire/_tutorial/README.md
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
# Wire Tutorial
|
||||||
|
|
||||||
|
Let's learn to use Wire by example. The [Wire guide][guide] provides thorough
|
||||||
|
documentation of the tool's usage. For readers eager to see Wire applied to a
|
||||||
|
larger server, the [guestbook sample in Go Cloud][guestbook] uses Wire to
|
||||||
|
initialize its components. Here we are going to build a small greeter program to
|
||||||
|
understand how to use Wire. The finished product may be found in the same
|
||||||
|
directory as this README.
|
||||||
|
|
||||||
|
[guestbook]: https://github.com/google/go-cloud/tree/master/samples/guestbook
|
||||||
|
[guide]: https://github.com/google/wire/blob/master/docs/guide.md
|
||||||
|
|
||||||
|
## A First Pass of Building the Greeter Program
|
||||||
|
|
||||||
|
Let's create a small program that simulates an event with a greeter greeting
|
||||||
|
guests with a particular message.
|
||||||
|
|
||||||
|
To start, we will create three types: 1) a message for a greeter, 2) a greeter
|
||||||
|
who conveys that message, and 3) an event that starts with the greeter greeting
|
||||||
|
guests. In this design, we have three `struct` types:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
type Message string
|
||||||
|
|
||||||
|
type Greeter struct {
|
||||||
|
// ... TBD
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
// ... TBD
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Message` type just wraps a string. For now, we will create a simple
|
||||||
|
initializer that always returns a hard-coded message:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func NewMessage() Message {
|
||||||
|
return Message("Hi there!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Our `Greeter` will need reference to the `Message`. So let's create an
|
||||||
|
initializer for our `Greeter` as well.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func NewGreeter(m Message) Greeter {
|
||||||
|
return Greeter{Message: m}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Greeter struct {
|
||||||
|
Message Message // <- adding a Message field
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the initializer we assign a `Message` field to `Greeter`. Now, we can use the
|
||||||
|
`Message` when we create a `Greet` method on `Greeter`:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (g Greeter) Greet() Message {
|
||||||
|
return g.Message
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we need our `Event` to have a `Greeter`, so we will create an initializer
|
||||||
|
for it as well.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func NewEvent(g Greeter) Event {
|
||||||
|
return Event{Greeter: g}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
Greeter Greeter // <- adding a Greeter field
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we add a method to start the `Event`:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (e Event) Start() {
|
||||||
|
msg := e.Greeter.Greet()
|
||||||
|
fmt.Println(msg)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Start` method holds the core of our small application: it tells the
|
||||||
|
greeter to issue a greeting and then prints that message to the screen.
|
||||||
|
|
||||||
|
Now that we have all the components of our application ready, let's see what it
|
||||||
|
takes to initialize all the components without using Wire. Our main function
|
||||||
|
would look like this:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func main() {
|
||||||
|
message := NewMessage()
|
||||||
|
greeter := NewGreeter(message)
|
||||||
|
event := NewEvent(greeter)
|
||||||
|
|
||||||
|
event.Start()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
First we create a message, then we create a greeter with that message, and
|
||||||
|
finally we create an event with that greeter. With all the initialization done,
|
||||||
|
we're ready to start our event.
|
||||||
|
|
||||||
|
We are using the [dependency injection][di] design principle. In practice, that
|
||||||
|
means we pass in whatever each component needs. This style of design lends
|
||||||
|
itself to writing easily tested code and makes it easy to swap out one
|
||||||
|
dependency with another.
|
||||||
|
|
||||||
|
[di]: https://stackoverflow.com/questions/130794/what-is-dependency-injection
|
||||||
|
|
||||||
|
## Using Wire to Generate Code
|
||||||
|
|
||||||
|
One downside to dependency injection is the need for so many initialization
|
||||||
|
steps. Let's see how we can use Wire to make the process of initializing our
|
||||||
|
components smoother.
|
||||||
|
|
||||||
|
Let's start by changing our `main` function to look like this:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func main() {
|
||||||
|
e := InitializeEvent()
|
||||||
|
|
||||||
|
e.Start()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, in a separate file called `wire.go` we will define `InitializeEvent`.
|
||||||
|
This is where things get interesting:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// wire.go
|
||||||
|
|
||||||
|
func InitializeEvent() Event {
|
||||||
|
wire.Build(NewEvent, NewGreeter, NewMessage)
|
||||||
|
return Event{}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rather than go through the trouble of initializing each component in turn and
|
||||||
|
passing it into the next one, we instead have a single call to `wire.Build`
|
||||||
|
passing in the initializers we want to use. In Wire, initializers are known as
|
||||||
|
"providers," functions which provide a particular type. We add a zero value for
|
||||||
|
`Event` as a return value to satisfy the compiler. Note that even if we add
|
||||||
|
values to `Event`, Wire will ignore them. In fact, the injector's purpose is to
|
||||||
|
provide information about which providers to use to construct an `Event` and so
|
||||||
|
we will exclude it from our final binary with a build constraint at the top of
|
||||||
|
the file:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Note, a [build constraint][constraint] requires a blank, trailing line.
|
||||||
|
|
||||||
|
In Wire parlance, `InitializeEvent` is an "injector." Now that we have our
|
||||||
|
injector complete, we are ready to use the `wire` command line tool.
|
||||||
|
|
||||||
|
Install the tool with:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
go install github.com/google/wire/cmd/wire@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in the same directory with the above code, simply run `wire`. Wire will
|
||||||
|
find the `InitializeEvent` injector and generate a function whose body is
|
||||||
|
filled out with all the necessary initialization steps. The result will be
|
||||||
|
written to a file named `wire_gen.go`.
|
||||||
|
|
||||||
|
Let's take a look at what Wire did for us:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// wire_gen.go
|
||||||
|
|
||||||
|
func InitializeEvent() Event {
|
||||||
|
message := NewMessage()
|
||||||
|
greeter := NewGreeter(message)
|
||||||
|
event := NewEvent(greeter)
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It looks just like what we wrote above! Now this is a simple example with just
|
||||||
|
three components, so writing the initializer by hand isn't too painful. Imagine
|
||||||
|
how useful Wire is for components that are much more complex. When working with
|
||||||
|
Wire, we will commit both `wire.go` and `wire_gen.go` to source control.
|
||||||
|
|
||||||
|
[constraint]: https://godoc.org/go/build#hdr-Build_Constraints
|
||||||
|
|
||||||
|
## Making Changes with Wire
|
||||||
|
|
||||||
|
To show a small part of how Wire handles more complex setups, let's refactor
|
||||||
|
our initializer for `Event` to return an error and see what happens.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func NewEvent(g Greeter) (Event, error) {
|
||||||
|
if g.Grumpy {
|
||||||
|
return Event{}, errors.New("could not create event: event greeter is grumpy")
|
||||||
|
}
|
||||||
|
return Event{Greeter: g}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We'll say that sometimes a `Greeter` might be grumpy and so we cannot create
|
||||||
|
an `Event`. The `NewGreeter` initializer now looks like this:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func NewGreeter(m Message) Greeter {
|
||||||
|
var grumpy bool
|
||||||
|
if time.Now().Unix()%2 == 0 {
|
||||||
|
grumpy = true
|
||||||
|
}
|
||||||
|
return Greeter{Message: m, Grumpy: grumpy}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We have added a `Grumpy` field to `Greeter` struct and if the invocation time
|
||||||
|
of the initializer is an even number of seconds since the Unix epoch, we will
|
||||||
|
create a grumpy greeter instead of a friendly one.
|
||||||
|
|
||||||
|
The `Greet` method then becomes:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (g Greeter) Greet() Message {
|
||||||
|
if g.Grumpy {
|
||||||
|
return Message("Go away!")
|
||||||
|
}
|
||||||
|
return g.Message
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you see how a grumpy `Greeter` is no good for an `Event`. So `NewEvent` may
|
||||||
|
fail. Our `main` must now take into account that `InitializeEvent` may in fact
|
||||||
|
fail:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func main() {
|
||||||
|
e, err := InitializeEvent()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to create event: %s\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
e.Start()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We also need to update `InitializeEvent` to add an `error` type to the return value:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// wire.go
|
||||||
|
|
||||||
|
func InitializeEvent() (Event, error) {
|
||||||
|
wire.Build(NewEvent, NewGreeter, NewMessage)
|
||||||
|
return Event{}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With the setup complete, we are ready to invoke the `wire` command again. Note,
|
||||||
|
that after running `wire` once to produce a `wire_gen.go` file, we may also use
|
||||||
|
`go generate`. Having run the command, our `wire_gen.go` file looks like
|
||||||
|
this:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// wire_gen.go
|
||||||
|
|
||||||
|
func InitializeEvent() (Event, error) {
|
||||||
|
message := NewMessage()
|
||||||
|
greeter := NewGreeter(message)
|
||||||
|
event, err := NewEvent(greeter)
|
||||||
|
if err != nil {
|
||||||
|
return Event{}, err
|
||||||
|
}
|
||||||
|
return event, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Wire has detected that the `NewEvent` provider may fail and has done the right
|
||||||
|
thing inside the generated code: it checks the error and returns early if one
|
||||||
|
is present.
|
||||||
|
|
||||||
|
## Changing the Injector Signature
|
||||||
|
|
||||||
|
As another improvement, let's look at how Wire generates code based on the
|
||||||
|
signature of the injector. Presently, we have hard-coded the message inside
|
||||||
|
`NewMessage`. In practice, it's much nicer to allow callers to change that
|
||||||
|
message however they see fit. So let's change `InitializeEvent` to look like
|
||||||
|
this:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func InitializeEvent(phrase string) (Event, error) {
|
||||||
|
wire.Build(NewEvent, NewGreeter, NewMessage)
|
||||||
|
return Event{}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now `InitializeEvent` allows callers to pass in the `phrase` for a `Greeter` to
|
||||||
|
use. We also add a `phrase` argument to `NewMessage`:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func NewMessage(phrase string) Message {
|
||||||
|
return Message(phrase)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After we run `wire` again, we will see that the tool has generated an
|
||||||
|
initializer which passes the `phrase` value as a `Message` into `Greeter`.
|
||||||
|
Neat!
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// wire_gen.go
|
||||||
|
|
||||||
|
func InitializeEvent(phrase string) (Event, error) {
|
||||||
|
message := NewMessage(phrase)
|
||||||
|
greeter := NewGreeter(message)
|
||||||
|
event, err := NewEvent(greeter)
|
||||||
|
if err != nil {
|
||||||
|
return Event{}, err
|
||||||
|
}
|
||||||
|
return event, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Wire inspects the arguments to the injector, sees that we added a string to the
|
||||||
|
list of arguments (e.g., `phrase`), and likewise sees that among all the
|
||||||
|
providers, `NewMessage` takes a string, and so it passes `phrase` into
|
||||||
|
`NewMessage`.
|
||||||
|
|
||||||
|
## Catching Mistakes with Helpful Errors
|
||||||
|
|
||||||
|
Let's also look at what happens when Wire detects mistakes in our code and see
|
||||||
|
how Wire's error messages help us correct any problems.
|
||||||
|
|
||||||
|
For example, when writing our injector `InitializeEvent`, let's say we forget
|
||||||
|
to add a provider for `Greeter`. Let's see what happens:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func InitializeEvent(phrase string) (Event, error) {
|
||||||
|
wire.Build(NewEvent, NewMessage) // woops! We forgot to add a provider for Greeter
|
||||||
|
return Event{}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Running `wire`, we see the following:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
# wrapping the error across lines for readability
|
||||||
|
$GOPATH/src/github.com/google/wire/_tutorial/wire.go:24:1:
|
||||||
|
inject InitializeEvent: no provider found for github.com/google/wire/_tutorial.Greeter
|
||||||
|
(required by provider of github.com/google/wire/_tutorial.Event)
|
||||||
|
wire: generate failed
|
||||||
|
```
|
||||||
|
|
||||||
|
Wire is telling us some useful information: it cannot find a provider for
|
||||||
|
`Greeter`. Note that the error message prints out the full path to the
|
||||||
|
`Greeter` type. It's also telling us the line number and injector name where
|
||||||
|
the problem occurred: line 24 inside `InitializeEvent`. In addition, the error
|
||||||
|
message tells us which provider needs a `Greeter`. It's the `Event` type. Once
|
||||||
|
we pass in a provider of `Greeter`, the problem will be solved.
|
||||||
|
|
||||||
|
Alternatively, what happens if we provide one too many providers to `wire.Build`?
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func NewEventNumber() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitializeEvent(phrase string) (Event, error) {
|
||||||
|
// woops! NewEventNumber is unused.
|
||||||
|
wire.Build(NewEvent, NewGreeter, NewMessage, NewEventNumber)
|
||||||
|
return Event{}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Wire helpfully tells us that we have an unused provider:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$GOPATH/src/github.com/google/wire/_tutorial/wire.go:24:1:
|
||||||
|
inject InitializeEvent: unused provider "NewEventNumber"
|
||||||
|
wire: generate failed
|
||||||
|
```
|
||||||
|
|
||||||
|
Deleting the unused provider from the call to `wire.Build` resolves the error.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Let's summarize what we have done here. First, we wrote a number of components
|
||||||
|
with corresponding initializers, or providers. Next, we created an injector
|
||||||
|
function, specifying which arguments it receives and which types it returns.
|
||||||
|
Then, we filled in the injector function with a call to `wire.Build` supplying
|
||||||
|
all necessary providers. Finally, we ran the `wire` command to generate code
|
||||||
|
that wires up all the different initializers. When we added an argument to the
|
||||||
|
injector and an error return value, running `wire` again made all the necessary
|
||||||
|
updates to our generated code.
|
||||||
|
|
||||||
|
The example here is small, but it demonstrates some of the power of Wire, and
|
||||||
|
how it takes much of the pain out of initializing code using dependency
|
||||||
|
injection. Furthermore, using Wire produced code that looks much like what we
|
||||||
|
would otherwise write. There are no bespoke types that commit a user to Wire.
|
||||||
|
Instead it's just generated code. We may do with it what we will. Finally,
|
||||||
|
another point worth considering is how easy it is to add new dependencies to
|
||||||
|
our component initialization. As long as we tell Wire how to provide (i.e.,
|
||||||
|
initialize) a component, we may add that component anywhere in the dependency
|
||||||
|
graph and Wire will handle the rest.
|
||||||
|
|
||||||
|
In closing, it is worth mentioning that Wire supports a number of additional
|
||||||
|
features not discussed here. Providers may be grouped in [provider sets][sets].
|
||||||
|
There is support for [binding interfaces][interfaces], [binding
|
||||||
|
values][values], as well as support for [cleanup functions][cleanup]. See the
|
||||||
|
[Advanced Features][advanced] section for more.
|
||||||
|
|
||||||
|
[advanced]: https://github.com/google/wire/blob/master/docs/guide.md#advanced-features
|
||||||
|
[cleanup]: https://github.com/google/wire/blob/master/docs/guide.md#cleanup-functions
|
||||||
|
[interfaces]: https://github.com/google/wire/blob/master/docs/guide.md#binding-interfaces
|
||||||
|
[sets]: https://github.com/google/wire/blob/master/docs/guide.md#defining-providers
|
||||||
|
[values]: https://github.com/google/wire/blob/master/docs/guide.md#binding-values
|
83
pkg/build/wire/_tutorial/main.go
Normal file
83
pkg/build/wire/_tutorial/main.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// The greeter binary simulates an event with greeters greeting guests.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Message is what greeters will use to greet guests.
|
||||||
|
type Message string
|
||||||
|
|
||||||
|
// NewMessage creates a default Message.
|
||||||
|
func NewMessage(phrase string) Message {
|
||||||
|
return Message(phrase)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGreeter initializes a Greeter. If the current epoch time is an even
|
||||||
|
// number, NewGreeter will create a grumpy Greeter.
|
||||||
|
func NewGreeter(m Message) Greeter {
|
||||||
|
var grumpy bool
|
||||||
|
if time.Now().Unix()%2 == 0 {
|
||||||
|
grumpy = true
|
||||||
|
}
|
||||||
|
return Greeter{Message: m, Grumpy: grumpy}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Greeter is the type charged with greeting guests.
|
||||||
|
type Greeter struct {
|
||||||
|
Grumpy bool
|
||||||
|
Message Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Greet produces a greeting for guests.
|
||||||
|
func (g Greeter) Greet() Message {
|
||||||
|
if g.Grumpy {
|
||||||
|
return Message("Go away!")
|
||||||
|
}
|
||||||
|
return g.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEvent creates an event with the specified greeter.
|
||||||
|
func NewEvent(g Greeter) (Event, error) {
|
||||||
|
if g.Grumpy {
|
||||||
|
return Event{}, errors.New("could not create event: event greeter is grumpy")
|
||||||
|
}
|
||||||
|
return Event{Greeter: g}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event is a gathering with greeters.
|
||||||
|
type Event struct {
|
||||||
|
Greeter Greeter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start ensures the event starts with greeting all guests.
|
||||||
|
func (e Event) Start() {
|
||||||
|
msg := e.Greeter.Greet()
|
||||||
|
fmt.Println(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
e, err := InitializeEvent("hi there!")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to create event: %s\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
e.Start()
|
||||||
|
}
|
28
pkg/build/wire/_tutorial/wire.go
Normal file
28
pkg/build/wire/_tutorial/wire.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build wireinject
|
||||||
|
// +build wireinject
|
||||||
|
|
||||||
|
// The build tag makes sure the stub is not built in the final build.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
|
||||||
|
// InitializeEvent creates an Event. It will error if the Event is staffed with
|
||||||
|
// a grumpy greeter.
|
||||||
|
func InitializeEvent(phrase string) (Event, error) {
|
||||||
|
wire.Build(NewEvent, NewGreeter, NewMessage)
|
||||||
|
return Event{}, nil
|
||||||
|
}
|
19
pkg/build/wire/_tutorial/wire_gen.go
Normal file
19
pkg/build/wire/_tutorial/wire_gen.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func InitializeEvent(phrase string) (Event, error) {
|
||||||
|
message := NewMessage(phrase)
|
||||||
|
greeter := NewGreeter(message)
|
||||||
|
event, err := NewEvent(greeter)
|
||||||
|
if err != nil {
|
||||||
|
return Event{}, err
|
||||||
|
}
|
||||||
|
return event, nil
|
||||||
|
}
|
609
pkg/build/wire/cmd/wire/main.go
Normal file
609
pkg/build/wire/cmd/wire/main.go
Normal file
@ -0,0 +1,609 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Wire is a compile-time dependency injection tool.
|
||||||
|
//
|
||||||
|
// For an overview, see https://github.com/google/wire/blob/master/README.md
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/subcommands"
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire/internal/wire"
|
||||||
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
subcommands.Register(subcommands.CommandsCommand(), "")
|
||||||
|
subcommands.Register(subcommands.FlagsCommand(), "")
|
||||||
|
subcommands.Register(subcommands.HelpCommand(), "")
|
||||||
|
subcommands.Register(&checkCmd{}, "")
|
||||||
|
subcommands.Register(&diffCmd{}, "")
|
||||||
|
subcommands.Register(&genCmd{}, "")
|
||||||
|
subcommands.Register(&showCmd{}, "")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Initialize the default logger to log to stderr.
|
||||||
|
log.SetFlags(0)
|
||||||
|
log.SetPrefix("wire: ")
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
// TODO(rvangent): Use subcommands's VisitCommands instead of hardcoded map,
|
||||||
|
// once there is a release that contains it:
|
||||||
|
// allCmds := map[string]bool{}
|
||||||
|
// subcommands.DefaultCommander.VisitCommands(func(_ *subcommands.CommandGroup, cmd subcommands.Command) { allCmds[cmd.Name()] = true })
|
||||||
|
allCmds := map[string]bool{
|
||||||
|
"commands": true, // builtin
|
||||||
|
"help": true, // builtin
|
||||||
|
"flags": true, // builtin
|
||||||
|
"check": true,
|
||||||
|
"diff": true,
|
||||||
|
"gen": true,
|
||||||
|
"show": true,
|
||||||
|
}
|
||||||
|
// Default to running the "gen" command.
|
||||||
|
if args := flag.Args(); len(args) == 0 || !allCmds[args[0]] {
|
||||||
|
genCmd := &genCmd{}
|
||||||
|
os.Exit(int(genCmd.Execute(context.Background(), flag.CommandLine)))
|
||||||
|
}
|
||||||
|
os.Exit(int(subcommands.Execute(context.Background())))
|
||||||
|
}
|
||||||
|
|
||||||
|
// packages returns the slice of packages to run wire over based on f.
|
||||||
|
// It defaults to ".".
|
||||||
|
func packages(f *flag.FlagSet) []string {
|
||||||
|
pkgs := f.Args()
|
||||||
|
if len(pkgs) == 0 {
|
||||||
|
pkgs = []string{"."}
|
||||||
|
}
|
||||||
|
return pkgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGenerateOptions returns an initialized wire.GenerateOptions, possibly
|
||||||
|
// with the Header option set.
|
||||||
|
func newGenerateOptions(headerFile string) (*wire.GenerateOptions, error) {
|
||||||
|
opts := new(wire.GenerateOptions)
|
||||||
|
if headerFile != "" {
|
||||||
|
var err error
|
||||||
|
opts.Header, err = ioutil.ReadFile(headerFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read header file %q: %v", headerFile, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type genCmd struct {
|
||||||
|
headerFile string
|
||||||
|
prefixFileName string
|
||||||
|
tags string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*genCmd) Name() string { return "gen" }
|
||||||
|
func (*genCmd) Synopsis() string {
|
||||||
|
return "generate the wire_gen.go file for each package"
|
||||||
|
}
|
||||||
|
func (*genCmd) Usage() string {
|
||||||
|
return `gen [packages]
|
||||||
|
|
||||||
|
Given one or more packages, gen creates the wire_gen.go file for each.
|
||||||
|
|
||||||
|
If no packages are listed, it defaults to ".".
|
||||||
|
`
|
||||||
|
}
|
||||||
|
func (cmd *genCmd) SetFlags(f *flag.FlagSet) {
|
||||||
|
f.StringVar(&cmd.headerFile, "header_file", "", "path to file to insert as a header in wire_gen.go")
|
||||||
|
f.StringVar(&cmd.prefixFileName, "output_file_prefix", "", "string to prepend to output file names.")
|
||||||
|
f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *genCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to get working directory: ", err)
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
opts, err := newGenerateOptions(cmd.headerFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.PrefixOutputFile = cmd.prefixFileName
|
||||||
|
opts.Tags = cmd.tags
|
||||||
|
|
||||||
|
outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f), opts)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
logErrors(errs)
|
||||||
|
log.Println("generate failed")
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
if len(outs) == 0 {
|
||||||
|
return subcommands.ExitSuccess
|
||||||
|
}
|
||||||
|
success := true
|
||||||
|
for _, out := range outs {
|
||||||
|
if len(out.Errs) > 0 {
|
||||||
|
logErrors(out.Errs)
|
||||||
|
log.Printf("%s: generate failed\n", out.PkgPath)
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
if len(out.Content) == 0 {
|
||||||
|
// No Wire output. Maybe errors, maybe no Wire directives.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := out.Commit(); err == nil {
|
||||||
|
log.Printf("%s: wrote %s\n", out.PkgPath, out.OutputPath)
|
||||||
|
} else {
|
||||||
|
log.Printf("%s: failed to write %s: %v\n", out.PkgPath, out.OutputPath, err)
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !success {
|
||||||
|
log.Println("at least one generate failure")
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
return subcommands.ExitSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
type diffCmd struct {
|
||||||
|
headerFile string
|
||||||
|
tags string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*diffCmd) Name() string { return "diff" }
|
||||||
|
func (*diffCmd) Synopsis() string {
|
||||||
|
return "output a diff between existing wire_gen.go files and what gen would generate"
|
||||||
|
}
|
||||||
|
func (*diffCmd) Usage() string {
|
||||||
|
return `diff [packages]
|
||||||
|
|
||||||
|
Given one or more packages, diff generates the content for their wire_gen.go
|
||||||
|
files and outputs the diff against the existing files.
|
||||||
|
|
||||||
|
If no packages are listed, it defaults to ".".
|
||||||
|
|
||||||
|
Similar to the diff command, it returns 0 if no diff, 1 if different, 2
|
||||||
|
plus an error if trouble.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
func (cmd *diffCmd) SetFlags(f *flag.FlagSet) {
|
||||||
|
f.StringVar(&cmd.headerFile, "header_file", "", "path to file to insert as a header in wire_gen.go")
|
||||||
|
f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild")
|
||||||
|
}
|
||||||
|
func (cmd *diffCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||||
|
const (
|
||||||
|
errReturn = subcommands.ExitStatus(2)
|
||||||
|
diffReturn = subcommands.ExitStatus(1)
|
||||||
|
)
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to get working directory: ", err)
|
||||||
|
return errReturn
|
||||||
|
}
|
||||||
|
opts, err := newGenerateOptions(cmd.headerFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.Tags = cmd.tags
|
||||||
|
|
||||||
|
outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f), opts)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
logErrors(errs)
|
||||||
|
log.Println("generate failed")
|
||||||
|
return errReturn
|
||||||
|
}
|
||||||
|
if len(outs) == 0 {
|
||||||
|
return subcommands.ExitSuccess
|
||||||
|
}
|
||||||
|
success := true
|
||||||
|
hadDiff := false
|
||||||
|
for _, out := range outs {
|
||||||
|
if len(out.Errs) > 0 {
|
||||||
|
logErrors(out.Errs)
|
||||||
|
log.Printf("%s: generate failed\n", out.PkgPath)
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
if len(out.Content) == 0 {
|
||||||
|
// No Wire output. Maybe errors, maybe no Wire directives.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Assumes the current file is empty if we can't read it.
|
||||||
|
cur, _ := ioutil.ReadFile(out.OutputPath)
|
||||||
|
if diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||||
|
A: difflib.SplitLines(string(cur)),
|
||||||
|
B: difflib.SplitLines(string(out.Content)),
|
||||||
|
}); err == nil {
|
||||||
|
if diff != "" {
|
||||||
|
// Print the actual diff to stdout, not stderr.
|
||||||
|
fmt.Printf("%s: diff from %s:\n%s\n", out.PkgPath, out.OutputPath, diff)
|
||||||
|
hadDiff = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("%s: failed to diff %s: %v\n", out.PkgPath, out.OutputPath, err)
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !success {
|
||||||
|
log.Println("at least one generate failure")
|
||||||
|
return errReturn
|
||||||
|
}
|
||||||
|
if hadDiff {
|
||||||
|
return diffReturn
|
||||||
|
}
|
||||||
|
return subcommands.ExitSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
type showCmd struct {
|
||||||
|
tags string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*showCmd) Name() string { return "show" }
|
||||||
|
func (*showCmd) Synopsis() string {
|
||||||
|
return "describe all top-level provider sets"
|
||||||
|
}
|
||||||
|
func (*showCmd) Usage() string {
|
||||||
|
return `show [packages]
|
||||||
|
|
||||||
|
Given one or more packages, show finds all the provider sets declared as
|
||||||
|
top-level variables and prints what other provider sets they import and what
|
||||||
|
outputs they can produce, given possible inputs. It also lists any injector
|
||||||
|
functions defined in the package.
|
||||||
|
|
||||||
|
If no packages are listed, it defaults to ".".
|
||||||
|
`
|
||||||
|
}
|
||||||
|
func (cmd *showCmd) SetFlags(f *flag.FlagSet) {
|
||||||
|
f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild")
|
||||||
|
}
|
||||||
|
func (cmd *showCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to get working directory: ", err)
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
info, errs := wire.Load(ctx, wd, os.Environ(), cmd.tags, packages(f))
|
||||||
|
if info != nil {
|
||||||
|
keys := make([]wire.ProviderSetID, 0, len(info.Sets))
|
||||||
|
for k := range info.Sets {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
if keys[i].ImportPath == keys[j].ImportPath {
|
||||||
|
return keys[i].VarName < keys[j].VarName
|
||||||
|
}
|
||||||
|
return keys[i].ImportPath < keys[j].ImportPath
|
||||||
|
})
|
||||||
|
for i, k := range keys {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
outGroups, imports := gather(info, k)
|
||||||
|
fmt.Println(k)
|
||||||
|
for _, imp := range sortSet(imports) {
|
||||||
|
fmt.Printf("\t%s\n", imp)
|
||||||
|
}
|
||||||
|
for i := range outGroups {
|
||||||
|
fmt.Printf("\tOutputs given %s:\n", outGroups[i].name)
|
||||||
|
out := make(map[string]token.Pos, outGroups[i].outputs.Len())
|
||||||
|
outGroups[i].outputs.Iterate(func(t types.Type, v interface{}) {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case *wire.Provider:
|
||||||
|
out[types.TypeString(t, nil)] = v.Pos
|
||||||
|
case *wire.Value:
|
||||||
|
out[types.TypeString(t, nil)] = v.Pos
|
||||||
|
case *wire.Field:
|
||||||
|
out[types.TypeString(t, nil)] = v.Pos
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
for _, t := range sortSet(out) {
|
||||||
|
fmt.Printf("\t\t%s\n", t)
|
||||||
|
fmt.Printf("\t\t\tat %v\n", info.Fset.Position(out[t]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(info.Injectors) > 0 {
|
||||||
|
injectors := append([]*wire.Injector(nil), info.Injectors...)
|
||||||
|
sort.Slice(injectors, func(i, j int) bool {
|
||||||
|
if injectors[i].ImportPath == injectors[j].ImportPath {
|
||||||
|
return injectors[i].FuncName < injectors[j].FuncName
|
||||||
|
}
|
||||||
|
return injectors[i].ImportPath < injectors[j].ImportPath
|
||||||
|
})
|
||||||
|
fmt.Println("\nInjectors:")
|
||||||
|
for _, in := range injectors {
|
||||||
|
fmt.Printf("\t%v\n", in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
logErrors(errs)
|
||||||
|
log.Println("error loading packages")
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
return subcommands.ExitSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
type checkCmd struct {
|
||||||
|
tags string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*checkCmd) Name() string { return "check" }
|
||||||
|
func (*checkCmd) Synopsis() string {
|
||||||
|
return "print any Wire errors found"
|
||||||
|
}
|
||||||
|
func (*checkCmd) Usage() string {
|
||||||
|
return `check [-tags tag,list] [packages]
|
||||||
|
|
||||||
|
Given one or more packages, check prints any type-checking or Wire errors
|
||||||
|
found with top-level variable provider sets or injector functions.
|
||||||
|
|
||||||
|
If no packages are listed, it defaults to ".".
|
||||||
|
`
|
||||||
|
}
|
||||||
|
func (cmd *checkCmd) SetFlags(f *flag.FlagSet) {
|
||||||
|
f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild")
|
||||||
|
}
|
||||||
|
func (cmd *checkCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to get working directory: ", err)
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
_, errs := wire.Load(ctx, wd, os.Environ(), cmd.tags, packages(f))
|
||||||
|
if len(errs) > 0 {
|
||||||
|
logErrors(errs)
|
||||||
|
log.Println("error loading packages")
|
||||||
|
return subcommands.ExitFailure
|
||||||
|
}
|
||||||
|
return subcommands.ExitSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
type outGroup struct {
|
||||||
|
name string
|
||||||
|
inputs *typeutil.Map // values are not important
|
||||||
|
outputs *typeutil.Map // values are *wire.Provider, *wire.Value, or *wire.Field
|
||||||
|
}
|
||||||
|
|
||||||
|
// gather flattens a provider set into outputs grouped by the inputs
|
||||||
|
// required to create them. As it flattens the provider set, it records
|
||||||
|
// the visited named provider sets as imports.
|
||||||
|
func gather(info *wire.Info, key wire.ProviderSetID) (_ []outGroup, imports map[string]struct{}) {
|
||||||
|
set := info.Sets[key]
|
||||||
|
hash := typeutil.MakeHasher()
|
||||||
|
|
||||||
|
// Find imports.
|
||||||
|
next := []*wire.ProviderSet{info.Sets[key]}
|
||||||
|
visited := make(map[*wire.ProviderSet]struct{})
|
||||||
|
imports = make(map[string]struct{})
|
||||||
|
for len(next) > 0 {
|
||||||
|
curr := next[len(next)-1]
|
||||||
|
next = next[:len(next)-1]
|
||||||
|
if _, found := visited[curr]; found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
visited[curr] = struct{}{}
|
||||||
|
if curr.VarName != "" && !(curr.PkgPath == key.ImportPath && curr.VarName == key.VarName) {
|
||||||
|
imports[formatProviderSetName(curr.PkgPath, curr.VarName)] = struct{}{}
|
||||||
|
}
|
||||||
|
next = append(next, curr.Imports...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth-first search to build groups.
|
||||||
|
var groups []outGroup
|
||||||
|
inputVisited := new(typeutil.Map) // values are int, indices into groups or -1 for input.
|
||||||
|
inputVisited.SetHasher(hash)
|
||||||
|
var stk []types.Type
|
||||||
|
for _, k := range set.Outputs() {
|
||||||
|
// Start a DFS by picking a random unvisited node.
|
||||||
|
if inputVisited.At(k) == nil {
|
||||||
|
stk = append(stk, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run DFS
|
||||||
|
dfs:
|
||||||
|
for len(stk) > 0 {
|
||||||
|
curr := stk[len(stk)-1]
|
||||||
|
stk = stk[:len(stk)-1]
|
||||||
|
if inputVisited.At(curr) != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch pv := set.For(curr); {
|
||||||
|
case pv.IsNil():
|
||||||
|
// This is an input.
|
||||||
|
inputVisited.Set(curr, -1)
|
||||||
|
case pv.IsArg():
|
||||||
|
// This is an injector argument.
|
||||||
|
inputVisited.Set(curr, -1)
|
||||||
|
case pv.IsProvider():
|
||||||
|
// Try to see if any args haven't been visited.
|
||||||
|
p := pv.Provider()
|
||||||
|
allPresent := true
|
||||||
|
for _, arg := range p.Args {
|
||||||
|
if inputVisited.At(arg.Type) == nil {
|
||||||
|
allPresent = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !allPresent {
|
||||||
|
stk = append(stk, curr)
|
||||||
|
for _, arg := range p.Args {
|
||||||
|
if inputVisited.At(arg.Type) == nil {
|
||||||
|
stk = append(stk, arg.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue dfs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build up set of input types, match to a group.
|
||||||
|
in := new(typeutil.Map)
|
||||||
|
in.SetHasher(hash)
|
||||||
|
for _, arg := range p.Args {
|
||||||
|
i := inputVisited.At(arg.Type).(int)
|
||||||
|
if i == -1 {
|
||||||
|
in.Set(arg.Type, true)
|
||||||
|
} else {
|
||||||
|
mergeTypeSets(in, groups[i].inputs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range groups {
|
||||||
|
if sameTypeKeys(groups[i].inputs, in) {
|
||||||
|
groups[i].outputs.Set(curr, p)
|
||||||
|
inputVisited.Set(curr, i)
|
||||||
|
continue dfs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out := new(typeutil.Map)
|
||||||
|
out.SetHasher(hash)
|
||||||
|
out.Set(curr, p)
|
||||||
|
inputVisited.Set(curr, len(groups))
|
||||||
|
groups = append(groups, outGroup{
|
||||||
|
inputs: in,
|
||||||
|
outputs: out,
|
||||||
|
})
|
||||||
|
case pv.IsValue():
|
||||||
|
v := pv.Value()
|
||||||
|
for i := range groups {
|
||||||
|
if groups[i].inputs.Len() == 0 {
|
||||||
|
groups[i].outputs.Set(curr, v)
|
||||||
|
inputVisited.Set(curr, i)
|
||||||
|
continue dfs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in := new(typeutil.Map)
|
||||||
|
in.SetHasher(hash)
|
||||||
|
out := new(typeutil.Map)
|
||||||
|
out.SetHasher(hash)
|
||||||
|
out.Set(curr, v)
|
||||||
|
inputVisited.Set(curr, len(groups))
|
||||||
|
groups = append(groups, outGroup{
|
||||||
|
inputs: in,
|
||||||
|
outputs: out,
|
||||||
|
})
|
||||||
|
case pv.IsField():
|
||||||
|
// Try to see if the parent struct hasn't been visited.
|
||||||
|
f := pv.Field()
|
||||||
|
if inputVisited.At(f.Parent) == nil {
|
||||||
|
stk = append(stk, curr, f.Parent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Build the input map for the parent struct.
|
||||||
|
in := new(typeutil.Map)
|
||||||
|
in.SetHasher(hash)
|
||||||
|
i := inputVisited.At(f.Parent).(int)
|
||||||
|
if i == -1 {
|
||||||
|
in.Set(f.Parent, true)
|
||||||
|
} else {
|
||||||
|
mergeTypeSets(in, groups[i].inputs)
|
||||||
|
}
|
||||||
|
// Group all fields together under the same parent struct.
|
||||||
|
for i := range groups {
|
||||||
|
if sameTypeKeys(groups[i].inputs, in) {
|
||||||
|
groups[i].outputs.Set(curr, f)
|
||||||
|
inputVisited.Set(curr, i)
|
||||||
|
continue dfs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out := new(typeutil.Map)
|
||||||
|
out.SetHasher(hash)
|
||||||
|
out.Set(curr, f)
|
||||||
|
inputVisited.Set(curr, len(groups))
|
||||||
|
groups = append(groups, outGroup{
|
||||||
|
inputs: in,
|
||||||
|
outputs: out,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name and sort groups.
|
||||||
|
for i := range groups {
|
||||||
|
if groups[i].inputs.Len() == 0 {
|
||||||
|
groups[i].name = "no inputs"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
instr := make([]string, 0, groups[i].inputs.Len())
|
||||||
|
groups[i].inputs.Iterate(func(k types.Type, _ interface{}) {
|
||||||
|
instr = append(instr, types.TypeString(k, nil))
|
||||||
|
})
|
||||||
|
sort.Strings(instr)
|
||||||
|
groups[i].name = strings.Join(instr, ", ")
|
||||||
|
}
|
||||||
|
sort.Slice(groups, func(i, j int) bool {
|
||||||
|
if groups[i].inputs.Len() == groups[j].inputs.Len() {
|
||||||
|
return groups[i].name < groups[j].name
|
||||||
|
}
|
||||||
|
return groups[i].inputs.Len() < groups[j].inputs.Len()
|
||||||
|
})
|
||||||
|
return groups, imports
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeTypeSets(dst, src *typeutil.Map) {
|
||||||
|
src.Iterate(func(k types.Type, _ interface{}) {
|
||||||
|
dst.Set(k, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameTypeKeys(a, b *typeutil.Map) bool {
|
||||||
|
if a.Len() != b.Len() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
same := true
|
||||||
|
a.Iterate(func(k types.Type, _ interface{}) {
|
||||||
|
if b.At(k) == nil {
|
||||||
|
same = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return same
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortSet(set interface{}) []string {
|
||||||
|
rv := reflect.ValueOf(set)
|
||||||
|
a := make([]string, 0, rv.Len())
|
||||||
|
keys := rv.MapKeys()
|
||||||
|
for _, k := range keys {
|
||||||
|
a = append(a, k.String())
|
||||||
|
}
|
||||||
|
sort.Strings(a)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatProviderSetName(importPath, varName string) string {
|
||||||
|
// Since varName is an identifier, it doesn't make sense to quote.
|
||||||
|
return strconv.Quote(importPath) + "." + varName
|
||||||
|
}
|
||||||
|
|
||||||
|
func logErrors(errs []error) {
|
||||||
|
for _, err := range errs {
|
||||||
|
log.Println(strings.Replace(err.Error(), "\n", "\n\t", -1))
|
||||||
|
}
|
||||||
|
}
|
BIN
pkg/build/wire/cmd/wire/wire
Executable file
BIN
pkg/build/wire/cmd/wire/wire
Executable file
Binary file not shown.
121
pkg/build/wire/docs/best-practices.md
Normal file
121
pkg/build/wire/docs/best-practices.md
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
# Best Practices
|
||||||
|
|
||||||
|
The following are practices we recommend for using Wire. This list will grow
|
||||||
|
over time.
|
||||||
|
|
||||||
|
## Distinguishing Types
|
||||||
|
|
||||||
|
If you need to inject a common type like `string`, create a new string type to
|
||||||
|
avoid conflicts with other providers. For example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MySQLConnectionString string
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options Structs
|
||||||
|
|
||||||
|
A provider function that includes many dependencies can pair the function with
|
||||||
|
an options struct.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Options struct {
|
||||||
|
// Messages is the set of recommended greetings.
|
||||||
|
Messages []Message
|
||||||
|
// Writer is the location to send greetings. nil goes to stdout.
|
||||||
|
Writer io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
var GreeterSet = wire.NewSet(wire.Struct(new(Options), "*"), NewGreeter)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Provider Sets in Libraries
|
||||||
|
|
||||||
|
When creating a provider set for use in a library, the only changes you can make
|
||||||
|
without breaking compatibility are:
|
||||||
|
|
||||||
|
- Change which provider a provider set uses to provide a specific output, as
|
||||||
|
long as it does not introduce a new input to the provider set. It may remove
|
||||||
|
inputs. However, note that existing injectors will use the old provider
|
||||||
|
until they are regenerated.
|
||||||
|
- Introduce a new output type into the provider set, but only if the type
|
||||||
|
itself is newly added. If the type is not new, it is possible that some
|
||||||
|
injector already has the output type included, which would cause a conflict.
|
||||||
|
|
||||||
|
All other changes are not safe. This includes:
|
||||||
|
|
||||||
|
- Requiring a new input in the provider set.
|
||||||
|
- Removing an output type from a provider set.
|
||||||
|
- Adding an existing output type into the provider set.
|
||||||
|
|
||||||
|
Instead of making one of these breaking changes, consider adding a new provider
|
||||||
|
set.
|
||||||
|
|
||||||
|
As an example, if you have a provider set like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var GreeterSet = wire.NewSet(NewStdoutGreeter)
|
||||||
|
|
||||||
|
func DefaultGreeter(ctx context.Context) *Greeter {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- Use `DefaultGreeter` instead of `NewStdoutGreeter` in `GreeterSet`.
|
||||||
|
- Create a new type `T` and add a provider for `T` to `GreeterSet`, as long as
|
||||||
|
`T` is introduced in the same commit/release as the provider is added.
|
||||||
|
|
||||||
|
You may not:
|
||||||
|
|
||||||
|
- Use `NewGreeter` instead of `NewStdoutGreeter` in `GreeterSet`. This both
|
||||||
|
adds an input type (`io.Writer`) and requires injectors to return an `error`
|
||||||
|
where the provider of `*Greeter` did not require this before.
|
||||||
|
- Remove `NewStdoutGreeter` from `GreeterSet`. Injectors depending on
|
||||||
|
`*Greeter` will be broken.
|
||||||
|
- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have
|
||||||
|
a provider for `io.Writer` which might conflict with this one.
|
||||||
|
|
||||||
|
As such, you should pick the output types in a library provider set carefully.
|
||||||
|
In general, prefer small provider sets in a library. For example, it is common
|
||||||
|
for a library provider set to contain a single provider function along with a
|
||||||
|
`wire.Bind` to the interface the return type implements. Avoiding larger
|
||||||
|
provider sets reduces the likelihood that applications will encounter conflicts.
|
||||||
|
To illustrate, imagine your library provides a client for a web service. While
|
||||||
|
it may be tempting to bundle a provider for `*http.Client` in a provider set for
|
||||||
|
your library's client, doing so would cause conflicts if every library did the
|
||||||
|
same. Instead, the library's provider set should only include the provider for
|
||||||
|
the API client, and let `*http.Client` be an input of the provider set.
|
||||||
|
|
||||||
|
## Mocking
|
||||||
|
|
||||||
|
There are two approaches for creating an injected app with mocked dependencies.
|
||||||
|
Examples of both approaches are shown
|
||||||
|
[here](https://github.com/google/wire/tree/master/internal/wire/testdata/ExampleWithMocks/foo).
|
||||||
|
|
||||||
|
### Approach A: Pass mocks to the injector
|
||||||
|
|
||||||
|
Create a test-only injector that takes all of the mocks as arguments; the
|
||||||
|
argument types must be the interface types the mocks are mocking. `wire.Build`
|
||||||
|
can't include providers for the mocked dependencies without creating conflicts,
|
||||||
|
so if you're using provider set(s) you will need to define one that doesn't
|
||||||
|
include the mocked types.
|
||||||
|
|
||||||
|
### Approach B: Return the mocks from the injector
|
||||||
|
|
||||||
|
Create a new struct that includes the app plus all of the dependencies you want
|
||||||
|
to mock. Create a test-only injector that returns this struct, give it providers
|
||||||
|
for the concrete mock types, and use `wire.Bind` to tell Wire that the concrete
|
||||||
|
mock types should be used to fulfill the appropriate interface.
|
130
pkg/build/wire/docs/faq.md
Normal file
130
pkg/build/wire/docs/faq.md
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
## How does Wire relate to other Go dependency injection tools?
|
||||||
|
|
||||||
|
Other dependency injection tools for Go like [dig][] or [facebookgo/inject][]
|
||||||
|
are based on reflection. Wire runs as a code generator, which means that the
|
||||||
|
injector works without making calls to a runtime library. This enables easier
|
||||||
|
introspection of initialization and correct cross-references for tooling like
|
||||||
|
[guru][].
|
||||||
|
|
||||||
|
[dig]: https://github.com/uber-go/dig
|
||||||
|
[facebookgo/inject]: https://github.com/facebookgo/inject
|
||||||
|
[guru]: https://golang.org/s/using-guru
|
||||||
|
|
||||||
|
## How does Wire relate to other non-Go dependency injection tools (like Dagger 2)?
|
||||||
|
|
||||||
|
Wire's approach was inspired by [Dagger 2][]. However, it is not the aim of Wire
|
||||||
|
to emulate dependency injection tools from other languages: the design space and
|
||||||
|
requirements are quite different. For example, the Go compiler does not support
|
||||||
|
anything like Java's annotation processing mechanisms. The difference in
|
||||||
|
languages and their idioms necessarily requires different approaches in
|
||||||
|
primitives and API.
|
||||||
|
|
||||||
|
[Dagger 2]: https://google.github.io/dagger/
|
||||||
|
|
||||||
|
## Why use pseudo-functions to create provider sets or injectors?
|
||||||
|
|
||||||
|
In the early prototypes, Wire directives were specially formatted comments. This
|
||||||
|
seemed appealing at first glance as this meant no compile-time or runtime
|
||||||
|
impact. However, this unstructured approach becomes opaque to other tooling not
|
||||||
|
written for Wire. Tools like [`gorename`][] or [guru][] would not be able to
|
||||||
|
recognize references to the identifiers existing in comments without being
|
||||||
|
specially modified to understand Wire's comment format. By moving the references
|
||||||
|
into no-op function calls, Wire interoperates seamlessly with other Go tooling.
|
||||||
|
|
||||||
|
[`gorename`]: https://godoc.org/golang.org/x/tools/cmd/gorename
|
||||||
|
|
||||||
|
## What if my dependency graph has two dependencies of the same type?
|
||||||
|
|
||||||
|
This most frequently appears with common types like `string`. An example of this
|
||||||
|
problem would be:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Foo struct { /* ... */ }
|
||||||
|
type Bar struct { /* ... */ }
|
||||||
|
|
||||||
|
func newFoo1() *Foo { /* ... */ }
|
||||||
|
func newFoo2() *Foo { /* ... */ }
|
||||||
|
func newBar(foo1 *Foo, foo2 *Foo) *Bar { /* ... */ }
|
||||||
|
|
||||||
|
func inject() *Bar {
|
||||||
|
// ERROR! Multiple providers for *Foo.
|
||||||
|
wire.Build(newFoo1, newFoo2, newBar)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Wire does not allow multiple providers for one type to exist in the transitive
|
||||||
|
closure of the providers presented to `wire.Build`, as this is usually a
|
||||||
|
mistake. For legitimate cases where you need multiple dependencies of the same
|
||||||
|
type, you need to invent a new type to call this other dependency. For example,
|
||||||
|
you can name OAuth credentials after the service they connect to. Once you have
|
||||||
|
a suitable different type, you can wrap and unwrap the type when plumbing it
|
||||||
|
through Wire. Continuing our above example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type OtherFoo Foo
|
||||||
|
|
||||||
|
func newOtherFoo() *OtherFoo {
|
||||||
|
// Call the original provider...
|
||||||
|
foo := newFoo2()
|
||||||
|
// ...then convert it to the new type.
|
||||||
|
return (*OtherFoo)(foo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideBar(foo1 *Foo, otherFoo *OtherFoo) *Bar {
|
||||||
|
// Convert the new type into the unwrapped type...
|
||||||
|
foo2 := (*Foo)(otherFoo)
|
||||||
|
// ...then use it to call the original provider.
|
||||||
|
return newBar(foo1, foo2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func inject() *Bar {
|
||||||
|
wire.Build(newFoo1, newOtherFoo, provideBar)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why does Wire forbid including the same provider multiple times?
|
||||||
|
|
||||||
|
Wire forbids this to remain consistent with the principle that specifying
|
||||||
|
multiple providers for the same type is an error. On the surface, Wire could
|
||||||
|
permit duplication, but this would introduce a few unintended consequences:
|
||||||
|
|
||||||
|
- Wire would have to specify what kinds of duplicates are permissible: are two
|
||||||
|
`wire.Value` calls ever considered to be the "same"?
|
||||||
|
- If a provider set changes the function it uses to provide a type, then this
|
||||||
|
could break an application, since it may introduce a new conflict between
|
||||||
|
another provider set that was specifying the "same" provider.
|
||||||
|
|
||||||
|
As such, we decided that the simpler behavior would be for this case to be an
|
||||||
|
error, knowing we can always relax this restriction later. The user can always
|
||||||
|
create a new provider set that does not have the conflicting type. A [proposed
|
||||||
|
subtract command][] would automate the toil in this process.
|
||||||
|
|
||||||
|
[proposed subtract command]: https://github.com/google/wire/issues/8
|
||||||
|
|
||||||
|
## Why does Wire require explicitly declare that a type provides an interface type?
|
||||||
|
|
||||||
|
The reason the binding is explicit is to avoid scenarios where adding a new type
|
||||||
|
to the provider graph that implements the same interface causes the graph to
|
||||||
|
break, because that can be surprising. While this does result in more typing,
|
||||||
|
the end-effect is that the developer's intent is more explicit in the code,
|
||||||
|
which we felt was most consistent with the Go philosophy.
|
||||||
|
|
||||||
|
There is an [open issue](https://github.com/google/wire/issues/242) to consider
|
||||||
|
improving this.
|
||||||
|
|
||||||
|
## Should I use Wire for small applications?
|
||||||
|
|
||||||
|
Probably not. Wire is designed to automate more intricate setup code found in
|
||||||
|
larger applications. For small applications, hand-wiring dependencies is
|
||||||
|
simpler.
|
||||||
|
|
||||||
|
## Who is using Wire?
|
||||||
|
|
||||||
|
Wire is still fairly new and doesn't have a large user base yet. However, we
|
||||||
|
have heard a lot of interest from Go users wanting to simplify their
|
||||||
|
applications. If your project or company uses Wire, please let us know by either
|
||||||
|
emailing us or sending a pull request amending this section.
|
465
pkg/build/wire/docs/guide.md
Normal file
465
pkg/build/wire/docs/guide.md
Normal file
@ -0,0 +1,465 @@
|
|||||||
|
# Wire User Guide
|
||||||
|
|
||||||
|
## Basics
|
||||||
|
|
||||||
|
Wire has two core concepts: providers and injectors.
|
||||||
|
|
||||||
|
### Defining Providers
|
||||||
|
|
||||||
|
The primary mechanism in Wire is the **provider**: a function that can produce a
|
||||||
|
value. These functions are ordinary Go code.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package foobarbaz
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvideFoo returns a Foo.
|
||||||
|
func ProvideFoo() Foo {
|
||||||
|
return Foo{X: 42}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Provider functions must be exported in order to be used from other packages,
|
||||||
|
just like ordinary functions.
|
||||||
|
|
||||||
|
Providers can specify dependencies with parameters:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package foobarbaz
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvideBar returns a Bar: a negative Foo.
|
||||||
|
func ProvideBar(foo Foo) Bar {
|
||||||
|
return Bar{X: -foo.X}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Providers can also return errors:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package foobarbaz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
type Baz struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvideBaz returns a value if Bar is not zero.
|
||||||
|
func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
|
||||||
|
if bar.X == 0 {
|
||||||
|
return Baz{}, errors.New("cannot provide baz when bar is zero")
|
||||||
|
}
|
||||||
|
return Baz{X: bar.X}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Providers can be grouped into **provider sets**. This is useful if several
|
||||||
|
providers will frequently be used together. To add these providers to a new set
|
||||||
|
called `SuperSet`, use the `wire.NewSet` function:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package foobarbaz
|
||||||
|
|
||||||
|
import (
|
||||||
|
// ...
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
var SuperSet = wire.NewSet(ProvideFoo, ProvideBar, ProvideBaz)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also add other provider sets into a provider set.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package foobarbaz
|
||||||
|
|
||||||
|
import (
|
||||||
|
// ...
|
||||||
|
"example.com/some/other/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
var MegaSet = wire.NewSet(SuperSet, pkg.OtherSet)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Injectors
|
||||||
|
|
||||||
|
An application wires up these providers with an **injector**: a function that
|
||||||
|
calls providers in dependency order. With Wire, you write the injector's
|
||||||
|
signature, then Wire generates the function's body.
|
||||||
|
|
||||||
|
An injector is declared by writing a function declaration whose body is a call
|
||||||
|
to `wire.Build`. The return values don't matter as long as they are of the
|
||||||
|
correct type. The values themselves will be ignored in the generated code. Let's
|
||||||
|
say that the above providers were defined in a package called
|
||||||
|
`example.com/foobarbaz`. The following would declare an injector to obtain a
|
||||||
|
`Baz`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// +build wireinject
|
||||||
|
// The build tag makes sure the stub is not built in the final build.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
"example.com/foobarbaz"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
|
||||||
|
wire.Build(foobarbaz.MegaSet)
|
||||||
|
return foobarbaz.Baz{}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Like providers, injectors can be parameterized on inputs (which then get sent to
|
||||||
|
providers) and can return errors. Arguments to `wire.Build` are the same as
|
||||||
|
`wire.NewSet`: they form a provider set. This is the provider set that gets used
|
||||||
|
during code generation for that injector.
|
||||||
|
|
||||||
|
Any non-injector declarations found in a file with injectors will be copied into
|
||||||
|
the generated file.
|
||||||
|
|
||||||
|
You can generate the injector by invoking Wire in the package directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
wire
|
||||||
|
```
|
||||||
|
|
||||||
|
Wire will produce an implementation of the injector in a file called
|
||||||
|
`wire_gen.go` that looks something like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//+build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"example.com/foobarbaz"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
|
||||||
|
foo := foobarbaz.ProvideFoo()
|
||||||
|
bar := foobarbaz.ProvideBar(foo)
|
||||||
|
baz, err := foobarbaz.ProvideBaz(ctx, bar)
|
||||||
|
if err != nil {
|
||||||
|
return foobarbaz.Baz{}, err
|
||||||
|
}
|
||||||
|
return baz, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, the output is very close to what a developer would write
|
||||||
|
themselves. Further, there is little dependency on Wire at runtime: all of the
|
||||||
|
written code is just normal Go code, and can be used without Wire.
|
||||||
|
|
||||||
|
Once `wire_gen.go` is created, you can regenerate it by running [`go generate`].
|
||||||
|
|
||||||
|
[`go generate`]: https://blog.golang.org/generate
|
||||||
|
|
||||||
|
## Advanced Features
|
||||||
|
|
||||||
|
The following features all build on top of the concepts of providers and
|
||||||
|
injectors.
|
||||||
|
|
||||||
|
### Binding Interfaces
|
||||||
|
|
||||||
|
Frequently, dependency injection is used to bind a concrete implementation for
|
||||||
|
an interface. Wire matches inputs to outputs via [type identity][], so the
|
||||||
|
inclination might be to create a provider that returns an interface type.
|
||||||
|
However, this would not be idiomatic, since the Go best practice is to
|
||||||
|
[return concrete types][]. Instead, you can declare an interface binding in a
|
||||||
|
provider set:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Fooer interface {
|
||||||
|
Foo() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyFooer string
|
||||||
|
|
||||||
|
func (b *MyFooer) Foo() string {
|
||||||
|
return string(*b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideMyFooer() *MyFooer {
|
||||||
|
b := new(MyFooer)
|
||||||
|
*b = "Hello, World!"
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar string
|
||||||
|
|
||||||
|
func provideBar(f Fooer) string {
|
||||||
|
// f will be a *MyFooer.
|
||||||
|
return f.Foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
var Set = wire.NewSet(
|
||||||
|
provideMyFooer,
|
||||||
|
wire.Bind(new(Fooer), new(*MyFooer)),
|
||||||
|
provideBar)
|
||||||
|
```
|
||||||
|
|
||||||
|
The first argument to `wire.Bind` is a pointer to a value of the desired
|
||||||
|
interface type and the second argument is a pointer to a value of the type that
|
||||||
|
implements the interface. Any set that includes an interface binding must also
|
||||||
|
have a provider in the same set that provides the concrete type.
|
||||||
|
|
||||||
|
[type identity]: https://golang.org/ref/spec#Type_identity
|
||||||
|
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
|
||||||
|
|
||||||
|
### Struct Providers
|
||||||
|
|
||||||
|
Structs can be constructed using provided types. Use the `wire.Struct` function
|
||||||
|
to construct a struct type and tell the injector which field(s) should be injected.
|
||||||
|
The injector will fill in each field using the provider for the field's type.
|
||||||
|
For the resulting struct type `S`, `wire.Struct` provides both `S` and `*S`. For
|
||||||
|
example, given the following providers:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Foo int
|
||||||
|
type Bar int
|
||||||
|
|
||||||
|
func ProvideFoo() Foo {/* ... */}
|
||||||
|
|
||||||
|
func ProvideBar() Bar {/* ... */}
|
||||||
|
|
||||||
|
type FooBar struct {
|
||||||
|
MyFoo Foo
|
||||||
|
MyBar Bar
|
||||||
|
}
|
||||||
|
|
||||||
|
var Set = wire.NewSet(
|
||||||
|
ProvideFoo,
|
||||||
|
ProvideBar,
|
||||||
|
wire.Struct(new(FooBar), "MyFoo", "MyBar"))
|
||||||
|
```
|
||||||
|
|
||||||
|
A generated injector for `FooBar` would look like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectFooBar() FooBar {
|
||||||
|
foo := ProvideFoo()
|
||||||
|
bar := ProvideBar()
|
||||||
|
fooBar := FooBar{
|
||||||
|
MyFoo: foo,
|
||||||
|
MyBar: bar,
|
||||||
|
}
|
||||||
|
return fooBar
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The first argument to `wire.Struct` is a pointer to the desired struct type and
|
||||||
|
the subsequent arguments are the names of fields to be injected. A special
|
||||||
|
string `"*"` can be used as a shortcut to tell the injector to inject all
|
||||||
|
fields. So `wire.Struct(new(FooBar), "*")` produces the same result as above.
|
||||||
|
|
||||||
|
For the above example, you can specify only injecting `"MyFoo"` by changing the
|
||||||
|
`Set` to:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var Set = wire.NewSet(
|
||||||
|
ProvideFoo,
|
||||||
|
wire.Struct(new(FooBar), "MyFoo"))
|
||||||
|
```
|
||||||
|
|
||||||
|
Then the generated injector for `FooBar` would look like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectFooBar() FooBar {
|
||||||
|
foo := ProvideFoo()
|
||||||
|
fooBar := FooBar{
|
||||||
|
MyFoo: foo,
|
||||||
|
}
|
||||||
|
return fooBar
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the injector returned a `*FooBar` instead of a `FooBar`, the generated injector
|
||||||
|
would look like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectFooBar() *FooBar {
|
||||||
|
foo := ProvideFoo()
|
||||||
|
fooBar := &FooBar{
|
||||||
|
MyFoo: foo,
|
||||||
|
}
|
||||||
|
return fooBar
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It is sometimes useful to prevent certain fields from being filled in by the
|
||||||
|
injector, especially when passing `*` to `wire.Struct`. You can tag a field with
|
||||||
|
`` `wire:"-"` `` to have Wire ignore such fields. For example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Foo struct {
|
||||||
|
mu sync.Mutex `wire:"-"`
|
||||||
|
Bar Bar
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When you provide the `Foo` type using `wire.Struct(new(Foo), "*")`, Wire will
|
||||||
|
automatically omit the `mu` field. Additionally, it is an error to explicitly
|
||||||
|
specify a prevented field as in `wire.Struct(new(Foo), "mu")`.
|
||||||
|
|
||||||
|
### Binding Values
|
||||||
|
|
||||||
|
Occasionally, it is useful to bind a basic value (usually `nil`) to a type.
|
||||||
|
Instead of having injectors depend on a throwaway provider function, you can add
|
||||||
|
a value expression to a provider set.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Foo struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
func injectFoo() Foo {
|
||||||
|
wire.Build(wire.Value(Foo{X: 42}))
|
||||||
|
return Foo{}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated injector would look like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectFoo() Foo {
|
||||||
|
foo := _wireFooValue
|
||||||
|
return foo
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_wireFooValue = Foo{X: 42}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
It's important to note that the expression will be copied to the injector's
|
||||||
|
package; references to variables will be evaluated during the injector package's
|
||||||
|
initialization. Wire will emit an error if the expression calls any functions or
|
||||||
|
receives from any channels.
|
||||||
|
|
||||||
|
For interface values, use `InterfaceValue`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectReader() io.Reader {
|
||||||
|
wire.Build(wire.InterfaceValue(new(io.Reader), os.Stdin))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use Fields of a Struct as Providers
|
||||||
|
|
||||||
|
Sometimes the providers the user wants are some fields of a struct. If you find
|
||||||
|
yourself writing a provider like `getS` in the example below to promote struct
|
||||||
|
fields into provided types:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Foo struct {
|
||||||
|
S string
|
||||||
|
N int
|
||||||
|
F float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func getS(foo Foo) string {
|
||||||
|
// Bad! Use wire.FieldsOf instead.
|
||||||
|
return foo.S
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideFoo() Foo {
|
||||||
|
return Foo{ S: "Hello, World!", N: 1, F: 3.14 }
|
||||||
|
}
|
||||||
|
|
||||||
|
func injectedMessage() string {
|
||||||
|
wire.Build(
|
||||||
|
provideFoo,
|
||||||
|
getS)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can instead use `wire.FieldsOf` to use those fields directly without writing
|
||||||
|
`getS`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectedMessage() string {
|
||||||
|
wire.Build(
|
||||||
|
provideFoo,
|
||||||
|
wire.FieldsOf(new(Foo), "S"))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated injector would look like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectedMessage() string {
|
||||||
|
foo := provideFoo()
|
||||||
|
string2 := foo.S
|
||||||
|
return string2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can add as many field names to a `wire.FieldsOf` function as you like.
|
||||||
|
For a given field type `T`, `FieldsOf` provides at least `T`; if the struct
|
||||||
|
argument is a pointer to a struct, then `FieldsOf` also provides `*T`.
|
||||||
|
|
||||||
|
### Cleanup functions
|
||||||
|
|
||||||
|
If a provider creates a value that needs to be cleaned up (e.g. closing a file),
|
||||||
|
then it can return a closure to clean up the resource. The injector will use
|
||||||
|
this to either return an aggregated cleanup function to the caller or to clean
|
||||||
|
up the resource if a provider called later in the injector's implementation
|
||||||
|
returns an error.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func provideFile(log Logger, path Path) (*os.File, func(), error) {
|
||||||
|
f, err := os.Open(string(path))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
cleanup := func() {
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
log.Log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f, cleanup, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A cleanup function is guaranteed to be called before the cleanup function of any
|
||||||
|
of the provider's inputs and must have the signature `func()`.
|
||||||
|
|
||||||
|
### Alternate Injector Syntax
|
||||||
|
|
||||||
|
If you grow weary of writing `return foobarbaz.Foo{}, nil` at the end of your
|
||||||
|
injector function declaration, you can instead write it more concisely with a
|
||||||
|
`panic`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectFoo() Foo {
|
||||||
|
panic(wire.Build(/* ... */))
|
||||||
|
}
|
||||||
|
```
|
10
pkg/build/wire/go.mod
Normal file
10
pkg/build/wire/go.mod
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module github.com/grafana/grafana/pkg/build/wire
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/go-cmp v0.6.0
|
||||||
|
github.com/google/subcommands v1.2.0
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
|
||||||
|
golang.org/x/tools v0.17.0
|
||||||
|
)
|
@ -1,14 +1,7 @@
|
|||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
|
|
||||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
|
||||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
||||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
|
||||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
|
||||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
@ -19,7 +12,6 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
@ -31,6 +23,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -55,8 +48,6 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s=
|
|
||||||
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
5
pkg/build/wire/internal/alldeps
Normal file
5
pkg/build/wire/internal/alldeps
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
github.com/google/subcommands
|
||||||
|
github.com/google/wire
|
||||||
|
github.com/pmezard/go-difflib
|
||||||
|
golang.org/x/mod
|
||||||
|
golang.org/x/tools
|
89
pkg/build/wire/internal/check_api_change.sh
Executable file
89
pkg/build/wire/internal/check_api_change.sh
Executable file
@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright 2019 The Wire Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# This script checks to see if there are any incompatible API changes on the
|
||||||
|
# current branch relative to the upstream branch.
|
||||||
|
# It fails if it finds any, unless there is a commit with BREAKING_CHANGE_OK
|
||||||
|
# in the first line of the commit message.
|
||||||
|
|
||||||
|
# This script expects:
|
||||||
|
# a) to be run at the root of the repository
|
||||||
|
# b) HEAD is pointing to a commit that merges between the pull request and the
|
||||||
|
# upstream branch (GITHUB_BASE_REF).
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
UPSTREAM_BRANCH="${GITHUB_BASE_REF:-master}"
|
||||||
|
echo "Checking for incompatible API changes relative to ${UPSTREAM_BRANCH}..."
|
||||||
|
|
||||||
|
MASTER_CLONE_DIR="$(mktemp -d)"
|
||||||
|
PKGINFO_BRANCH=$(mktemp)
|
||||||
|
PKGINFO_MASTER=$(mktemp)
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
rm -rf "$MASTER_CLONE_DIR"
|
||||||
|
rm -f "$PKGINFO_BRANCH"
|
||||||
|
rm -f "$PKGINFO_MASTER"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# Install apidiff.
|
||||||
|
go install golang.org/x/exp/cmd/apidiff@latest
|
||||||
|
|
||||||
|
git clone -b "$UPSTREAM_BRANCH" . "$MASTER_CLONE_DIR" &> /dev/null
|
||||||
|
|
||||||
|
incompatible_change_pkgs=()
|
||||||
|
PKGS=$(cd "$MASTER_CLONE_DIR"; go list ./... | grep -v test | grep -v internal)
|
||||||
|
for pkg in $PKGS; do
|
||||||
|
echo " Testing ${pkg}..."
|
||||||
|
|
||||||
|
# Compute export data for the current branch.
|
||||||
|
package_deleted=0
|
||||||
|
apidiff -w "$PKGINFO_BRANCH" "$pkg" || package_deleted=1
|
||||||
|
if [[ $package_deleted -eq 1 ]]; then
|
||||||
|
echo " Package ${pkg} was deleted! Recording as an incompatible change.";
|
||||||
|
incompatible_change_pkgs+=(${pkg});
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compute export data for master@HEAD.
|
||||||
|
(cd "$MASTER_CLONE_DIR"; apidiff -w "$PKGINFO_MASTER" "$pkg")
|
||||||
|
|
||||||
|
# Print all changes for posterity.
|
||||||
|
apidiff "$PKGINFO_MASTER" "$PKGINFO_BRANCH"
|
||||||
|
|
||||||
|
# Note if there's an incompatible change.
|
||||||
|
ic=$(apidiff -incompatible "$PKGINFO_MASTER" "$PKGINFO_BRANCH")
|
||||||
|
if [ ! -z "$ic" ]; then
|
||||||
|
incompatible_change_pkgs+=("$pkg");
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#incompatible_change_pkgs[@]} -eq 0 ]; then
|
||||||
|
# No incompatible changes, we are good.
|
||||||
|
echo "OK: No incompatible changes found."
|
||||||
|
exit 0;
|
||||||
|
fi
|
||||||
|
echo "Found breaking API change(s) in: ${incompatible_change_pkgs[*]}."
|
||||||
|
|
||||||
|
# Found incompatible changes; see if they were declared as OK via a commit.
|
||||||
|
if git cherry -v master | grep -q "BREAKING_CHANGE_OK"; then
|
||||||
|
echo "Allowing them due to a commit message with BREAKING_CHANGE_OK.";
|
||||||
|
exit 0;
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "FAIL. If this is expected and OK, you can pass this check by adding a commit with BREAKING_CHANGE_OK in the first line of the message."
|
||||||
|
exit 1
|
||||||
|
|
25
pkg/build/wire/internal/listdeps.sh
Executable file
25
pkg/build/wire/internal/listdeps.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright 2019 The Wire Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# To run this script manually to update alldeps:
|
||||||
|
#
|
||||||
|
# $ internal/listdeps.sh > internal/alldeps
|
||||||
|
#
|
||||||
|
# Important note: there are changes in module tooling behavior between go 1.11
|
||||||
|
# and go 1.12; please make sure to use the same version of Go as used by Github
|
||||||
|
# Actions (see .github/workflows/tests.yml) when updating the alldeps file.
|
||||||
|
go list -deps -f '{{with .Module}}{{.Path}}{{end}}' ./... | sort | uniq
|
80
pkg/build/wire/internal/runtests.sh
Executable file
80
pkg/build/wire/internal/runtests.sh
Executable file
@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright 2019 The Wire Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# https://coderwall.com/p/fkfaqq/safer-bash-scripts-with-set-euxo-pipefail
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
echo "usage: runtests.sh" 1>&2
|
||||||
|
exit 64
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run Go tests. Only do coverage for the Linux build
|
||||||
|
# because it is slow, and codecov will only save the last one anyway.
|
||||||
|
result=0
|
||||||
|
if [[ "${RUNNER_OS:-}" == "Linux" ]]; then
|
||||||
|
echo "Running Go tests (with coverage)..."
|
||||||
|
go test -mod=readonly -race -coverpkg=./... -coverprofile=coverage.out ./... || result=1
|
||||||
|
if [ -f coverage.out ] && [ $result -eq 0 ]; then
|
||||||
|
bash <(curl -s https://codecov.io/bash)
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Running Go tests..."
|
||||||
|
go test -mod=readonly -race ./... || result=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# No need to run other checks on OSs other than linux.
|
||||||
|
# We default RUNNER_OS to "Linux" so that we don't abort here when run locally.
|
||||||
|
if [[ "${RUNNER_OS:-Linux}" != "Linux" ]]; then
|
||||||
|
exit $result
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Ensuring .go files are formatted with gofmt -s..."
|
||||||
|
mapfile -t go_files < <(find . -name '*.go' -type f | grep -v testdata)
|
||||||
|
DIFF="$(gofmt -s -d "${go_files[@]}")"
|
||||||
|
if [ -n "$DIFF" ]; then
|
||||||
|
echo "FAIL: please run gofmt -s and commit the result"
|
||||||
|
echo "$DIFF";
|
||||||
|
result=1;
|
||||||
|
else
|
||||||
|
echo "OK"
|
||||||
|
fi;
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure that the code has no extra dependencies (including transitive
|
||||||
|
# dependencies) that we're not already aware of by comparing with
|
||||||
|
# ./internal/alldeps
|
||||||
|
#
|
||||||
|
# Whenever project dependencies change, rerun ./internal/listdeps.sh
|
||||||
|
echo
|
||||||
|
echo "Ensuring that there are no dependencies not listed in ./internal/alldeps..."
|
||||||
|
(./internal/listdeps.sh | diff ./internal/alldeps - && echo "OK") || {
|
||||||
|
echo "FAIL: dependencies changed; run: internal/listdeps.sh > internal/alldeps"
|
||||||
|
# Module behavior may differ across versions.
|
||||||
|
echo "using the latest go version."
|
||||||
|
result=1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# For pull requests, check if there are undeclared incompatible API changes.
|
||||||
|
# Skip this if we're already going to fail since it is expensive.
|
||||||
|
# CURRENTLY BROKEN
|
||||||
|
# if [[ ${result} -eq 0 ]] && [[ ! -z "${GITHUB_HEAD_REF:-x}" ]]; then
|
||||||
|
# echo
|
||||||
|
# ./internal/check_api_change.sh || result=1;
|
||||||
|
# fi
|
||||||
|
|
||||||
|
exit $result
|
521
pkg/build/wire/internal/wire/analyze.go
Normal file
521
pkg/build/wire/internal/wire/analyze.go
Normal file
@ -0,0 +1,521 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type callKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
funcProviderCall callKind = iota
|
||||||
|
structProvider
|
||||||
|
valueExpr
|
||||||
|
selectorExpr
|
||||||
|
)
|
||||||
|
|
||||||
|
// A call represents a step of an injector function. It may be either a
|
||||||
|
// function call or a composite struct literal, depending on the value
|
||||||
|
// of kind.
|
||||||
|
type call struct {
|
||||||
|
// kind indicates the code pattern to use.
|
||||||
|
kind callKind
|
||||||
|
|
||||||
|
// out is the type this step produces.
|
||||||
|
out types.Type
|
||||||
|
|
||||||
|
// pkg and name identify one of the following:
|
||||||
|
// 1) the provider to call for kind == funcProviderCall;
|
||||||
|
// 2) the type to construct for kind == structProvider;
|
||||||
|
// 3) the name to select for kind == selectorExpr.
|
||||||
|
pkg *types.Package
|
||||||
|
name string
|
||||||
|
|
||||||
|
// args is a list of arguments to call the provider with. Each element is:
|
||||||
|
// a) one of the givens (args[i] < len(given)),
|
||||||
|
// b) the result of a previous provider call (args[i] >= len(given))
|
||||||
|
//
|
||||||
|
// This will be nil for kind == valueExpr.
|
||||||
|
//
|
||||||
|
// If kind == selectorExpr, then the length of this slice will be 1 and the
|
||||||
|
// "argument" will be the value to access fields from.
|
||||||
|
args []int
|
||||||
|
|
||||||
|
// varargs is true if the provider function is variadic.
|
||||||
|
varargs bool
|
||||||
|
|
||||||
|
// fieldNames maps the arguments to struct field names.
|
||||||
|
// This will only be set if kind == structProvider.
|
||||||
|
fieldNames []string
|
||||||
|
|
||||||
|
// ins is the list of types this call receives as arguments.
|
||||||
|
// This will be nil for kind == valueExpr.
|
||||||
|
ins []types.Type
|
||||||
|
|
||||||
|
// The following are only set for kind == funcProviderCall:
|
||||||
|
|
||||||
|
// hasCleanup is true if the provider call returns a cleanup function.
|
||||||
|
hasCleanup bool
|
||||||
|
// hasErr is true if the provider call returns an error.
|
||||||
|
hasErr bool
|
||||||
|
|
||||||
|
// The following are only set for kind == valueExpr:
|
||||||
|
|
||||||
|
valueExpr ast.Expr
|
||||||
|
valueTypeInfo *types.Info
|
||||||
|
|
||||||
|
// The following are only set for kind == selectorExpr:
|
||||||
|
|
||||||
|
ptrToField bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// solve finds the sequence of calls required to produce an output type
|
||||||
|
// with an optional set of provided inputs.
|
||||||
|
func solve(fset *token.FileSet, out types.Type, given *types.Tuple, set *ProviderSet) ([]call, []error) {
|
||||||
|
ec := new(errorCollector)
|
||||||
|
|
||||||
|
// Start building the mapping of type to local variable of the given type.
|
||||||
|
// The first len(given) local variables are the given types.
|
||||||
|
index := new(typeutil.Map)
|
||||||
|
for i := 0; i < given.Len(); i++ {
|
||||||
|
index.Set(given.At(i).Type(), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Topological sort of the directed graph defined by the providers
|
||||||
|
// using a depth-first search using a stack. Provider set graphs are
|
||||||
|
// guaranteed to be acyclic. An index value of errAbort indicates that
|
||||||
|
// the type was visited, but failed due to an error added to ec.
|
||||||
|
errAbort := errors.New("failed to visit")
|
||||||
|
var used []*providerSetSrc
|
||||||
|
var calls []call
|
||||||
|
type frame struct {
|
||||||
|
t types.Type
|
||||||
|
from types.Type
|
||||||
|
up *frame
|
||||||
|
}
|
||||||
|
stk := []frame{{t: out}}
|
||||||
|
dfs:
|
||||||
|
for len(stk) > 0 {
|
||||||
|
curr := stk[len(stk)-1]
|
||||||
|
stk = stk[:len(stk)-1]
|
||||||
|
if index.At(curr.t) != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pv := set.For(curr.t)
|
||||||
|
if pv.IsNil() {
|
||||||
|
if curr.from == nil {
|
||||||
|
ec.add(fmt.Errorf("no provider found for %s, output of injector", types.TypeString(curr.t, nil)))
|
||||||
|
index.Set(curr.t, errAbort)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sb := new(strings.Builder)
|
||||||
|
fmt.Fprintf(sb, "no provider found for %s", types.TypeString(curr.t, nil))
|
||||||
|
for f := curr.up; f != nil; f = f.up {
|
||||||
|
fmt.Fprintf(sb, "\nneeded by %s in %s", types.TypeString(f.t, nil), set.srcMap.At(f.t).(*providerSetSrc).description(fset, f.t))
|
||||||
|
}
|
||||||
|
ec.add(errors.New(sb.String()))
|
||||||
|
index.Set(curr.t, errAbort)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
src := set.srcMap.At(curr.t).(*providerSetSrc)
|
||||||
|
used = append(used, src)
|
||||||
|
if concrete := pv.Type(); !types.Identical(concrete, curr.t) {
|
||||||
|
// Interface binding does not create a call.
|
||||||
|
i := index.At(concrete)
|
||||||
|
if i == nil {
|
||||||
|
stk = append(stk, curr, frame{t: concrete, from: curr.t, up: &curr})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
index.Set(curr.t, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pv := set.For(curr.t); {
|
||||||
|
case pv.IsArg():
|
||||||
|
// Continue, already added to stk.
|
||||||
|
case pv.IsProvider():
|
||||||
|
p := pv.Provider()
|
||||||
|
// Ensure that all argument types have been visited. If not, push them
|
||||||
|
// on the stack in reverse order so that calls are added in argument
|
||||||
|
// order.
|
||||||
|
visitedArgs := true
|
||||||
|
for i := len(p.Args) - 1; i >= 0; i-- {
|
||||||
|
a := p.Args[i]
|
||||||
|
if index.At(a.Type) == nil {
|
||||||
|
if visitedArgs {
|
||||||
|
// Make sure to re-visit this type after visiting all arguments.
|
||||||
|
stk = append(stk, curr)
|
||||||
|
visitedArgs = false
|
||||||
|
}
|
||||||
|
stk = append(stk, frame{t: a.Type, from: curr.t, up: &curr})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !visitedArgs {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
args := make([]int, len(p.Args))
|
||||||
|
ins := make([]types.Type, len(p.Args))
|
||||||
|
for i := range p.Args {
|
||||||
|
ins[i] = p.Args[i].Type
|
||||||
|
v := index.At(p.Args[i].Type)
|
||||||
|
if v == errAbort {
|
||||||
|
index.Set(curr.t, errAbort)
|
||||||
|
continue dfs
|
||||||
|
}
|
||||||
|
args[i] = v.(int)
|
||||||
|
}
|
||||||
|
index.Set(curr.t, given.Len()+len(calls))
|
||||||
|
kind := funcProviderCall
|
||||||
|
fieldNames := []string(nil)
|
||||||
|
if p.IsStruct {
|
||||||
|
kind = structProvider
|
||||||
|
for _, arg := range p.Args {
|
||||||
|
fieldNames = append(fieldNames, arg.FieldName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calls = append(calls, call{
|
||||||
|
kind: kind,
|
||||||
|
pkg: p.Pkg,
|
||||||
|
name: p.Name,
|
||||||
|
args: args,
|
||||||
|
varargs: p.Varargs,
|
||||||
|
fieldNames: fieldNames,
|
||||||
|
ins: ins,
|
||||||
|
out: curr.t,
|
||||||
|
hasCleanup: p.HasCleanup,
|
||||||
|
hasErr: p.HasErr,
|
||||||
|
})
|
||||||
|
case pv.IsValue():
|
||||||
|
v := pv.Value()
|
||||||
|
index.Set(curr.t, given.Len()+len(calls))
|
||||||
|
calls = append(calls, call{
|
||||||
|
kind: valueExpr,
|
||||||
|
out: curr.t,
|
||||||
|
valueExpr: v.expr,
|
||||||
|
valueTypeInfo: v.info,
|
||||||
|
})
|
||||||
|
case pv.IsField():
|
||||||
|
f := pv.Field()
|
||||||
|
if index.At(f.Parent) == nil {
|
||||||
|
// Fields have one dependency which is the parent struct. Make
|
||||||
|
// sure to visit it first if it is not already visited.
|
||||||
|
stk = append(stk, curr, frame{t: f.Parent, from: curr.t, up: &curr})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
index.Set(curr.t, given.Len()+len(calls))
|
||||||
|
v := index.At(f.Parent)
|
||||||
|
if v == errAbort {
|
||||||
|
index.Set(curr.t, errAbort)
|
||||||
|
continue dfs
|
||||||
|
}
|
||||||
|
// Use args[0] to store the position of the parent struct.
|
||||||
|
args := []int{v.(int)}
|
||||||
|
// If f.Out has 2 elements and curr.t is the 2nd one, then the call must
|
||||||
|
// provide a pointer to the field.
|
||||||
|
ptrToField := len(f.Out) == 2 && types.Identical(curr.t, f.Out[1])
|
||||||
|
calls = append(calls, call{
|
||||||
|
kind: selectorExpr,
|
||||||
|
pkg: f.Pkg,
|
||||||
|
name: f.Name,
|
||||||
|
out: curr.t,
|
||||||
|
args: args,
|
||||||
|
ptrToField: ptrToField,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic("unknown return value from ProviderSet.For")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ec.errors) > 0 {
|
||||||
|
return nil, ec.errors
|
||||||
|
}
|
||||||
|
if errs := verifyArgsUsed(set, used); len(errs) > 0 {
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
return calls, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyArgsUsed ensures that all of the arguments in set were used during solve.
|
||||||
|
func verifyArgsUsed(set *ProviderSet, used []*providerSetSrc) []error {
|
||||||
|
var errs []error
|
||||||
|
for _, imp := range set.Imports {
|
||||||
|
found := false
|
||||||
|
for _, u := range used {
|
||||||
|
if u.Import == imp {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
if imp.VarName == "" {
|
||||||
|
errs = append(errs, errors.New("unused provider set"))
|
||||||
|
} else {
|
||||||
|
errs = append(errs, fmt.Errorf("unused provider set %q", imp.VarName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, p := range set.Providers {
|
||||||
|
found := false
|
||||||
|
for _, u := range used {
|
||||||
|
if u.Provider == p {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
errs = append(errs, fmt.Errorf("unused provider %q", p.Pkg.Name()+"."+p.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range set.Values {
|
||||||
|
found := false
|
||||||
|
for _, u := range used {
|
||||||
|
if u.Value == v {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
errs = append(errs, fmt.Errorf("unused value of type %s", types.TypeString(v.Out, nil)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, b := range set.Bindings {
|
||||||
|
found := false
|
||||||
|
for _, u := range used {
|
||||||
|
if u.Binding == b {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
errs = append(errs, fmt.Errorf("unused interface binding to type %s", types.TypeString(b.Iface, nil)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, f := range set.Fields {
|
||||||
|
found := false
|
||||||
|
for _, u := range used {
|
||||||
|
if u.Field == f {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
errs = append(errs, fmt.Errorf("unused field %q.%s", f.Parent, f.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildProviderMap creates the providerMap and srcMap fields for a given
|
||||||
|
// provider set. The given provider set's providerMap and srcMap fields are
|
||||||
|
// ignored.
|
||||||
|
func buildProviderMap(fset *token.FileSet, hasher typeutil.Hasher, set *ProviderSet) (*typeutil.Map, *typeutil.Map, []error) {
|
||||||
|
providerMap := new(typeutil.Map)
|
||||||
|
providerMap.SetHasher(hasher)
|
||||||
|
srcMap := new(typeutil.Map) // to *providerSetSrc
|
||||||
|
srcMap.SetHasher(hasher)
|
||||||
|
|
||||||
|
ec := new(errorCollector)
|
||||||
|
// Process injector arguments.
|
||||||
|
if set.InjectorArgs != nil {
|
||||||
|
givens := set.InjectorArgs.Tuple
|
||||||
|
for i := 0; i < givens.Len(); i++ {
|
||||||
|
typ := givens.At(i).Type()
|
||||||
|
arg := &InjectorArg{Args: set.InjectorArgs, Index: i}
|
||||||
|
src := &providerSetSrc{InjectorArg: arg}
|
||||||
|
if prevSrc := srcMap.At(typ); prevSrc != nil {
|
||||||
|
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
providerMap.Set(typ, &ProvidedType{t: typ, a: arg})
|
||||||
|
srcMap.Set(typ, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Process imports, verifying that there are no conflicts between sets.
|
||||||
|
for _, imp := range set.Imports {
|
||||||
|
src := &providerSetSrc{Import: imp}
|
||||||
|
imp.providerMap.Iterate(func(k types.Type, v interface{}) {
|
||||||
|
if prevSrc := srcMap.At(k); prevSrc != nil {
|
||||||
|
ec.add(bindingConflictError(fset, k, set, src, prevSrc.(*providerSetSrc)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
providerMap.Set(k, v)
|
||||||
|
srcMap.Set(k, src)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(ec.errors) > 0 {
|
||||||
|
return nil, nil, ec.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process non-binding providers in new set.
|
||||||
|
for _, p := range set.Providers {
|
||||||
|
src := &providerSetSrc{Provider: p}
|
||||||
|
for _, typ := range p.Out {
|
||||||
|
if prevSrc := srcMap.At(typ); prevSrc != nil {
|
||||||
|
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
providerMap.Set(typ, &ProvidedType{t: typ, p: p})
|
||||||
|
srcMap.Set(typ, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range set.Values {
|
||||||
|
src := &providerSetSrc{Value: v}
|
||||||
|
if prevSrc := srcMap.At(v.Out); prevSrc != nil {
|
||||||
|
ec.add(bindingConflictError(fset, v.Out, set, src, prevSrc.(*providerSetSrc)))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
providerMap.Set(v.Out, &ProvidedType{t: v.Out, v: v})
|
||||||
|
srcMap.Set(v.Out, src)
|
||||||
|
}
|
||||||
|
for _, f := range set.Fields {
|
||||||
|
src := &providerSetSrc{Field: f}
|
||||||
|
for _, typ := range f.Out {
|
||||||
|
if prevSrc := srcMap.At(typ); prevSrc != nil {
|
||||||
|
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
providerMap.Set(typ, &ProvidedType{t: typ, f: f})
|
||||||
|
srcMap.Set(typ, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ec.errors) > 0 {
|
||||||
|
return nil, nil, ec.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process bindings in set. Must happen after the other providers to
|
||||||
|
// ensure the concrete type is being provided.
|
||||||
|
for _, b := range set.Bindings {
|
||||||
|
src := &providerSetSrc{Binding: b}
|
||||||
|
if prevSrc := srcMap.At(b.Iface); prevSrc != nil {
|
||||||
|
ec.add(bindingConflictError(fset, b.Iface, set, src, prevSrc.(*providerSetSrc)))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
concrete := providerMap.At(b.Provided)
|
||||||
|
if concrete == nil {
|
||||||
|
setName := set.VarName
|
||||||
|
if setName == "" {
|
||||||
|
setName = "provider set"
|
||||||
|
}
|
||||||
|
ec.add(notePosition(fset.Position(b.Pos), fmt.Errorf("wire.Bind of concrete type %q to interface %q, but %s does not include a provider for %q", b.Provided, b.Iface, setName, b.Provided)))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
providerMap.Set(b.Iface, concrete)
|
||||||
|
srcMap.Set(b.Iface, src)
|
||||||
|
}
|
||||||
|
if len(ec.errors) > 0 {
|
||||||
|
return nil, nil, ec.errors
|
||||||
|
}
|
||||||
|
return providerMap, srcMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyAcyclic(providerMap *typeutil.Map, hasher typeutil.Hasher) []error {
|
||||||
|
// We must visit every provider type inside provider map, but we don't
|
||||||
|
// have a well-defined starting point and there may be several
|
||||||
|
// distinct graphs. Thus, we start a depth-first search at every
|
||||||
|
// provider, but keep a shared record of visited providers to avoid
|
||||||
|
// duplicating work.
|
||||||
|
visited := new(typeutil.Map) // to bool
|
||||||
|
visited.SetHasher(hasher)
|
||||||
|
ec := new(errorCollector)
|
||||||
|
// Sort output types so that errors about cycles are consistent.
|
||||||
|
outputs := providerMap.Keys()
|
||||||
|
sort.Slice(outputs, func(i, j int) bool { return types.TypeString(outputs[i], nil) < types.TypeString(outputs[j], nil) })
|
||||||
|
for _, root := range outputs {
|
||||||
|
// Depth-first search using a stack of trails through the provider map.
|
||||||
|
stk := [][]types.Type{{root}}
|
||||||
|
for len(stk) > 0 {
|
||||||
|
curr := stk[len(stk)-1]
|
||||||
|
stk = stk[:len(stk)-1]
|
||||||
|
head := curr[len(curr)-1]
|
||||||
|
if v, _ := visited.At(head).(bool); v {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
visited.Set(head, true)
|
||||||
|
x := providerMap.At(head)
|
||||||
|
if x == nil {
|
||||||
|
// Leaf: input.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pt := x.(*ProvidedType)
|
||||||
|
switch {
|
||||||
|
case pt.IsValue():
|
||||||
|
// Leaf: values do not have dependencies.
|
||||||
|
case pt.IsArg():
|
||||||
|
// Injector arguments do not have dependencies.
|
||||||
|
case pt.IsProvider() || pt.IsField():
|
||||||
|
var args []types.Type
|
||||||
|
if pt.IsProvider() {
|
||||||
|
for _, arg := range pt.Provider().Args {
|
||||||
|
args = append(args, arg.Type)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args = append(args, pt.Field().Parent)
|
||||||
|
}
|
||||||
|
for _, a := range args {
|
||||||
|
hasCycle := false
|
||||||
|
for i, b := range curr {
|
||||||
|
if types.Identical(a, b) {
|
||||||
|
sb := new(strings.Builder)
|
||||||
|
fmt.Fprintf(sb, "cycle for %s:\n", types.TypeString(a, nil))
|
||||||
|
for j := i; j < len(curr); j++ {
|
||||||
|
t := providerMap.At(curr[j]).(*ProvidedType)
|
||||||
|
if t.IsProvider() {
|
||||||
|
p := t.Provider()
|
||||||
|
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Pkg.Path(), p.Name)
|
||||||
|
} else {
|
||||||
|
p := t.Field()
|
||||||
|
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Parent, p.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(sb, "%s", types.TypeString(a, nil))
|
||||||
|
ec.add(errors.New(sb.String()))
|
||||||
|
hasCycle = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasCycle {
|
||||||
|
next := append(append([]types.Type(nil), curr...), a)
|
||||||
|
stk = append(stk, next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("invalid provider map value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ec.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindingConflictError creates a new error describing multiple bindings
|
||||||
|
// for the same output type.
|
||||||
|
func bindingConflictError(fset *token.FileSet, typ types.Type, set *ProviderSet, cur, prev *providerSetSrc) error {
|
||||||
|
sb := new(strings.Builder)
|
||||||
|
if set.VarName != "" {
|
||||||
|
fmt.Fprintf(sb, "%s has ", set.VarName)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(sb, "multiple bindings for %s\n", types.TypeString(typ, nil))
|
||||||
|
fmt.Fprintf(sb, "current:\n<- %s\n", strings.Join(cur.trace(fset, typ), "\n<- "))
|
||||||
|
fmt.Fprintf(sb, "previous:\n<- %s", strings.Join(prev.trace(fset, typ), "\n<- "))
|
||||||
|
return notePosition(fset.Position(set.Pos), errors.New(sb.String()))
|
||||||
|
}
|
493
pkg/build/wire/internal/wire/copyast.go
Normal file
493
pkg/build/wire/internal/wire/copyast.go
Normal file
@ -0,0 +1,493 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// copyAST performs a deep copy of an AST. *ast.Ident identity will be
|
||||||
|
// preserved.
|
||||||
|
//
|
||||||
|
// This allows using astutil.Apply to rewrite an AST without modifying
|
||||||
|
// the original AST.
|
||||||
|
func copyAST(original ast.Node) ast.Node {
|
||||||
|
// This function is necessarily long. No utility function exists to do this
|
||||||
|
// clone, as most any attempt would need to have customization options, which
|
||||||
|
// would need to be as expressive as Apply. A possibility to shorten the code
|
||||||
|
// here would be to use reflection, but that trades clarity for shorter code.
|
||||||
|
|
||||||
|
m := make(map[ast.Node]ast.Node)
|
||||||
|
astutil.Apply(original, nil, func(c *astutil.Cursor) bool {
|
||||||
|
switch node := c.Node().(type) {
|
||||||
|
case nil:
|
||||||
|
// No-op.
|
||||||
|
case *ast.ArrayType:
|
||||||
|
m[node] = &ast.ArrayType{
|
||||||
|
Lbrack: node.Lbrack,
|
||||||
|
Len: exprFromMap(m, node.Len),
|
||||||
|
Elt: exprFromMap(m, node.Elt),
|
||||||
|
}
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
m[node] = &ast.AssignStmt{
|
||||||
|
Lhs: copyExprList(m, node.Lhs),
|
||||||
|
TokPos: node.TokPos,
|
||||||
|
Tok: node.Tok,
|
||||||
|
Rhs: copyExprList(m, node.Rhs),
|
||||||
|
}
|
||||||
|
case *ast.BadDecl:
|
||||||
|
m[node] = &ast.BadDecl{
|
||||||
|
From: node.From,
|
||||||
|
To: node.To,
|
||||||
|
}
|
||||||
|
case *ast.BadExpr:
|
||||||
|
m[node] = &ast.BadExpr{
|
||||||
|
From: node.From,
|
||||||
|
To: node.To,
|
||||||
|
}
|
||||||
|
case *ast.BadStmt:
|
||||||
|
m[node] = &ast.BadStmt{
|
||||||
|
From: node.From,
|
||||||
|
To: node.To,
|
||||||
|
}
|
||||||
|
case *ast.BasicLit:
|
||||||
|
m[node] = &ast.BasicLit{
|
||||||
|
ValuePos: node.ValuePos,
|
||||||
|
Kind: node.Kind,
|
||||||
|
Value: node.Value,
|
||||||
|
}
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
m[node] = &ast.BinaryExpr{
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
OpPos: node.OpPos,
|
||||||
|
Op: node.Op,
|
||||||
|
Y: exprFromMap(m, node.Y),
|
||||||
|
}
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
m[node] = &ast.BlockStmt{
|
||||||
|
Lbrace: node.Lbrace,
|
||||||
|
List: copyStmtList(m, node.List),
|
||||||
|
Rbrace: node.Rbrace,
|
||||||
|
}
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
m[node] = &ast.BranchStmt{
|
||||||
|
TokPos: node.TokPos,
|
||||||
|
Tok: node.Tok,
|
||||||
|
Label: identFromMap(m, node.Label),
|
||||||
|
}
|
||||||
|
case *ast.CallExpr:
|
||||||
|
m[node] = &ast.CallExpr{
|
||||||
|
Fun: exprFromMap(m, node.Fun),
|
||||||
|
Lparen: node.Lparen,
|
||||||
|
Args: copyExprList(m, node.Args),
|
||||||
|
Ellipsis: node.Ellipsis,
|
||||||
|
Rparen: node.Rparen,
|
||||||
|
}
|
||||||
|
case *ast.CaseClause:
|
||||||
|
m[node] = &ast.CaseClause{
|
||||||
|
Case: node.Case,
|
||||||
|
List: copyExprList(m, node.List),
|
||||||
|
Colon: node.Colon,
|
||||||
|
Body: copyStmtList(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.ChanType:
|
||||||
|
m[node] = &ast.ChanType{
|
||||||
|
Begin: node.Begin,
|
||||||
|
Arrow: node.Arrow,
|
||||||
|
Dir: node.Dir,
|
||||||
|
Value: exprFromMap(m, node.Value),
|
||||||
|
}
|
||||||
|
case *ast.CommClause:
|
||||||
|
m[node] = &ast.CommClause{
|
||||||
|
Case: node.Case,
|
||||||
|
Comm: stmtFromMap(m, node.Comm),
|
||||||
|
Colon: node.Colon,
|
||||||
|
Body: copyStmtList(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.Comment:
|
||||||
|
m[node] = &ast.Comment{
|
||||||
|
Slash: node.Slash,
|
||||||
|
Text: node.Text,
|
||||||
|
}
|
||||||
|
case *ast.CommentGroup:
|
||||||
|
cg := new(ast.CommentGroup)
|
||||||
|
if node.List != nil {
|
||||||
|
cg.List = make([]*ast.Comment, len(node.List))
|
||||||
|
for i := range node.List {
|
||||||
|
cg.List[i] = m[node.List[i]].(*ast.Comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[node] = cg
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
m[node] = &ast.CompositeLit{
|
||||||
|
Type: exprFromMap(m, node.Type),
|
||||||
|
Lbrace: node.Lbrace,
|
||||||
|
Elts: copyExprList(m, node.Elts),
|
||||||
|
Rbrace: node.Rbrace,
|
||||||
|
}
|
||||||
|
case *ast.DeclStmt:
|
||||||
|
m[node] = &ast.DeclStmt{
|
||||||
|
Decl: m[node.Decl].(ast.Decl),
|
||||||
|
}
|
||||||
|
case *ast.DeferStmt:
|
||||||
|
m[node] = &ast.DeferStmt{
|
||||||
|
Defer: node.Defer,
|
||||||
|
Call: callExprFromMap(m, node.Call),
|
||||||
|
}
|
||||||
|
case *ast.Ellipsis:
|
||||||
|
m[node] = &ast.Ellipsis{
|
||||||
|
Ellipsis: node.Ellipsis,
|
||||||
|
Elt: exprFromMap(m, node.Elt),
|
||||||
|
}
|
||||||
|
case *ast.EmptyStmt:
|
||||||
|
m[node] = &ast.EmptyStmt{
|
||||||
|
Semicolon: node.Semicolon,
|
||||||
|
Implicit: node.Implicit,
|
||||||
|
}
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
m[node] = &ast.ExprStmt{
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
}
|
||||||
|
case *ast.Field:
|
||||||
|
m[node] = &ast.Field{
|
||||||
|
Doc: commentGroupFromMap(m, node.Doc),
|
||||||
|
Names: copyIdentList(m, node.Names),
|
||||||
|
Type: exprFromMap(m, node.Type),
|
||||||
|
Tag: basicLitFromMap(m, node.Tag),
|
||||||
|
Comment: commentGroupFromMap(m, node.Comment),
|
||||||
|
}
|
||||||
|
case *ast.FieldList:
|
||||||
|
fl := &ast.FieldList{
|
||||||
|
Opening: node.Opening,
|
||||||
|
Closing: node.Closing,
|
||||||
|
}
|
||||||
|
if node.List != nil {
|
||||||
|
fl.List = make([]*ast.Field, len(node.List))
|
||||||
|
for i := range node.List {
|
||||||
|
fl.List[i] = m[node.List[i]].(*ast.Field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[node] = fl
|
||||||
|
case *ast.ForStmt:
|
||||||
|
m[node] = &ast.ForStmt{
|
||||||
|
For: node.For,
|
||||||
|
Init: stmtFromMap(m, node.Init),
|
||||||
|
Cond: exprFromMap(m, node.Cond),
|
||||||
|
Post: stmtFromMap(m, node.Post),
|
||||||
|
Body: blockStmtFromMap(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
m[node] = &ast.FuncDecl{
|
||||||
|
Doc: commentGroupFromMap(m, node.Doc),
|
||||||
|
Recv: fieldListFromMap(m, node.Recv),
|
||||||
|
Name: identFromMap(m, node.Name),
|
||||||
|
Type: funcTypeFromMap(m, node.Type),
|
||||||
|
Body: blockStmtFromMap(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.FuncLit:
|
||||||
|
m[node] = &ast.FuncLit{
|
||||||
|
Type: funcTypeFromMap(m, node.Type),
|
||||||
|
Body: blockStmtFromMap(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.FuncType:
|
||||||
|
m[node] = &ast.FuncType{
|
||||||
|
Func: node.Func,
|
||||||
|
Params: fieldListFromMap(m, node.Params),
|
||||||
|
Results: fieldListFromMap(m, node.Results),
|
||||||
|
}
|
||||||
|
case *ast.GenDecl:
|
||||||
|
decl := &ast.GenDecl{
|
||||||
|
Doc: commentGroupFromMap(m, node.Doc),
|
||||||
|
TokPos: node.TokPos,
|
||||||
|
Tok: node.Tok,
|
||||||
|
Lparen: node.Lparen,
|
||||||
|
Rparen: node.Rparen,
|
||||||
|
}
|
||||||
|
if node.Specs != nil {
|
||||||
|
decl.Specs = make([]ast.Spec, len(node.Specs))
|
||||||
|
for i := range node.Specs {
|
||||||
|
decl.Specs[i] = m[node.Specs[i]].(ast.Spec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[node] = decl
|
||||||
|
case *ast.GoStmt:
|
||||||
|
m[node] = &ast.GoStmt{
|
||||||
|
Go: node.Go,
|
||||||
|
Call: callExprFromMap(m, node.Call),
|
||||||
|
}
|
||||||
|
case *ast.Ident:
|
||||||
|
// Keep identifiers the same identity so they can be conveniently
|
||||||
|
// used with the original *types.Info.
|
||||||
|
m[node] = node
|
||||||
|
case *ast.IfStmt:
|
||||||
|
m[node] = &ast.IfStmt{
|
||||||
|
If: node.If,
|
||||||
|
Init: stmtFromMap(m, node.Init),
|
||||||
|
Cond: exprFromMap(m, node.Cond),
|
||||||
|
Body: blockStmtFromMap(m, node.Body),
|
||||||
|
Else: stmtFromMap(m, node.Else),
|
||||||
|
}
|
||||||
|
case *ast.ImportSpec:
|
||||||
|
m[node] = &ast.ImportSpec{
|
||||||
|
Doc: commentGroupFromMap(m, node.Doc),
|
||||||
|
Name: identFromMap(m, node.Name),
|
||||||
|
Path: basicLitFromMap(m, node.Path),
|
||||||
|
Comment: commentGroupFromMap(m, node.Comment),
|
||||||
|
EndPos: node.EndPos,
|
||||||
|
}
|
||||||
|
case *ast.IncDecStmt:
|
||||||
|
m[node] = &ast.IncDecStmt{
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
TokPos: node.TokPos,
|
||||||
|
Tok: node.Tok,
|
||||||
|
}
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
m[node] = &ast.IndexExpr{
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
Lbrack: node.Lbrack,
|
||||||
|
Index: exprFromMap(m, node.Index),
|
||||||
|
Rbrack: node.Rbrack,
|
||||||
|
}
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
m[node] = &ast.InterfaceType{
|
||||||
|
Interface: node.Interface,
|
||||||
|
Methods: fieldListFromMap(m, node.Methods),
|
||||||
|
Incomplete: node.Incomplete,
|
||||||
|
}
|
||||||
|
case *ast.KeyValueExpr:
|
||||||
|
m[node] = &ast.KeyValueExpr{
|
||||||
|
Key: exprFromMap(m, node.Key),
|
||||||
|
Colon: node.Colon,
|
||||||
|
Value: exprFromMap(m, node.Value),
|
||||||
|
}
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
m[node] = &ast.LabeledStmt{
|
||||||
|
Label: identFromMap(m, node.Label),
|
||||||
|
Colon: node.Colon,
|
||||||
|
Stmt: stmtFromMap(m, node.Stmt),
|
||||||
|
}
|
||||||
|
case *ast.MapType:
|
||||||
|
m[node] = &ast.MapType{
|
||||||
|
Map: node.Map,
|
||||||
|
Key: exprFromMap(m, node.Key),
|
||||||
|
Value: exprFromMap(m, node.Value),
|
||||||
|
}
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
m[node] = &ast.ParenExpr{
|
||||||
|
Lparen: node.Lparen,
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
Rparen: node.Rparen,
|
||||||
|
}
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
m[node] = &ast.RangeStmt{
|
||||||
|
For: node.For,
|
||||||
|
Key: exprFromMap(m, node.Key),
|
||||||
|
Value: exprFromMap(m, node.Value),
|
||||||
|
TokPos: node.TokPos,
|
||||||
|
Tok: node.Tok,
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
Body: blockStmtFromMap(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.ReturnStmt:
|
||||||
|
m[node] = &ast.ReturnStmt{
|
||||||
|
Return: node.Return,
|
||||||
|
Results: copyExprList(m, node.Results),
|
||||||
|
}
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
m[node] = &ast.SelectStmt{
|
||||||
|
Select: node.Select,
|
||||||
|
Body: blockStmtFromMap(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
m[node] = &ast.SelectorExpr{
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
Sel: identFromMap(m, node.Sel),
|
||||||
|
}
|
||||||
|
case *ast.SendStmt:
|
||||||
|
m[node] = &ast.SendStmt{
|
||||||
|
Chan: exprFromMap(m, node.Chan),
|
||||||
|
Arrow: node.Arrow,
|
||||||
|
Value: exprFromMap(m, node.Value),
|
||||||
|
}
|
||||||
|
case *ast.SliceExpr:
|
||||||
|
m[node] = &ast.SliceExpr{
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
Lbrack: node.Lbrack,
|
||||||
|
Low: exprFromMap(m, node.Low),
|
||||||
|
High: exprFromMap(m, node.High),
|
||||||
|
Max: exprFromMap(m, node.Max),
|
||||||
|
Slice3: node.Slice3,
|
||||||
|
Rbrack: node.Rbrack,
|
||||||
|
}
|
||||||
|
case *ast.StarExpr:
|
||||||
|
m[node] = &ast.StarExpr{
|
||||||
|
Star: node.Star,
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
}
|
||||||
|
case *ast.StructType:
|
||||||
|
m[node] = &ast.StructType{
|
||||||
|
Struct: node.Struct,
|
||||||
|
Fields: fieldListFromMap(m, node.Fields),
|
||||||
|
Incomplete: node.Incomplete,
|
||||||
|
}
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
m[node] = &ast.SwitchStmt{
|
||||||
|
Switch: node.Switch,
|
||||||
|
Init: stmtFromMap(m, node.Init),
|
||||||
|
Tag: exprFromMap(m, node.Tag),
|
||||||
|
Body: blockStmtFromMap(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.TypeAssertExpr:
|
||||||
|
m[node] = &ast.TypeAssertExpr{
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
Lparen: node.Lparen,
|
||||||
|
Type: exprFromMap(m, node.Type),
|
||||||
|
Rparen: node.Rparen,
|
||||||
|
}
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
m[node] = &ast.TypeSpec{
|
||||||
|
Doc: commentGroupFromMap(m, node.Doc),
|
||||||
|
Name: identFromMap(m, node.Name),
|
||||||
|
Assign: node.Assign,
|
||||||
|
Type: exprFromMap(m, node.Type),
|
||||||
|
Comment: commentGroupFromMap(m, node.Comment),
|
||||||
|
}
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
m[node] = &ast.TypeSwitchStmt{
|
||||||
|
Switch: node.Switch,
|
||||||
|
Init: stmtFromMap(m, node.Init),
|
||||||
|
Assign: stmtFromMap(m, node.Assign),
|
||||||
|
Body: blockStmtFromMap(m, node.Body),
|
||||||
|
}
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
m[node] = &ast.UnaryExpr{
|
||||||
|
OpPos: node.OpPos,
|
||||||
|
Op: node.Op,
|
||||||
|
X: exprFromMap(m, node.X),
|
||||||
|
}
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
m[node] = &ast.ValueSpec{
|
||||||
|
Doc: commentGroupFromMap(m, node.Doc),
|
||||||
|
Names: copyIdentList(m, node.Names),
|
||||||
|
Type: exprFromMap(m, node.Type),
|
||||||
|
Values: copyExprList(m, node.Values),
|
||||||
|
Comment: commentGroupFromMap(m, node.Comment),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled AST node: %T", node))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return m[original]
|
||||||
|
}
|
||||||
|
|
||||||
|
func commentGroupFromMap(m map[ast.Node]ast.Node, key *ast.CommentGroup) *ast.CommentGroup {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(*ast.CommentGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exprFromMap(m map[ast.Node]ast.Node, key ast.Expr) ast.Expr {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(ast.Expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stmtFromMap(m map[ast.Node]ast.Node, key ast.Stmt) ast.Stmt {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(ast.Stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func identFromMap(m map[ast.Node]ast.Node, key *ast.Ident) *ast.Ident {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(*ast.Ident)
|
||||||
|
}
|
||||||
|
|
||||||
|
func blockStmtFromMap(m map[ast.Node]ast.Node, key *ast.BlockStmt) *ast.BlockStmt {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(*ast.BlockStmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldListFromMap(m map[ast.Node]ast.Node, key *ast.FieldList) *ast.FieldList {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(*ast.FieldList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func callExprFromMap(m map[ast.Node]ast.Node, key *ast.CallExpr) *ast.CallExpr {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(*ast.CallExpr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func basicLitFromMap(m map[ast.Node]ast.Node, key *ast.BasicLit) *ast.BasicLit {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(*ast.BasicLit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func funcTypeFromMap(m map[ast.Node]ast.Node, key *ast.FuncType) *ast.FuncType {
|
||||||
|
if key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[key].(*ast.FuncType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyExprList(m map[ast.Node]ast.Node, exprs []ast.Expr) []ast.Expr {
|
||||||
|
if exprs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newExprs := make([]ast.Expr, len(exprs))
|
||||||
|
for i := range exprs {
|
||||||
|
newExprs[i] = m[exprs[i]].(ast.Expr)
|
||||||
|
}
|
||||||
|
return newExprs
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyStmtList(m map[ast.Node]ast.Node, stmts []ast.Stmt) []ast.Stmt {
|
||||||
|
if stmts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newStmts := make([]ast.Stmt, len(stmts))
|
||||||
|
for i := range stmts {
|
||||||
|
newStmts[i] = m[stmts[i]].(ast.Stmt)
|
||||||
|
}
|
||||||
|
return newStmts
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyIdentList(m map[ast.Node]ast.Node, idents []*ast.Ident) []*ast.Ident {
|
||||||
|
if idents == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newIdents := make([]*ast.Ident, len(idents))
|
||||||
|
for i := range idents {
|
||||||
|
newIdents[i] = m[idents[i]].(*ast.Ident)
|
||||||
|
}
|
||||||
|
return newIdents
|
||||||
|
}
|
84
pkg/build/wire/internal/wire/errors.go
Normal file
84
pkg/build/wire/internal/wire/errors.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// errorCollector manages a list of errors. The zero value is an empty list.
|
||||||
|
type errorCollector struct {
|
||||||
|
errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
// add appends any non-nil errors to the collector.
|
||||||
|
func (ec *errorCollector) add(errs ...error) {
|
||||||
|
for _, e := range errs {
|
||||||
|
if e != nil {
|
||||||
|
ec.errors = append(ec.errors, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapErrors returns a new slice that wraps any errors using the given function.
|
||||||
|
func mapErrors(errs []error, f func(error) error) []error {
|
||||||
|
if len(errs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newErrs := make([]error, len(errs))
|
||||||
|
for i := range errs {
|
||||||
|
newErrs[i] = f(errs[i])
|
||||||
|
}
|
||||||
|
return newErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// A wireErr is an error with an optional position.
|
||||||
|
type wireErr struct {
|
||||||
|
error error
|
||||||
|
position token.Position
|
||||||
|
}
|
||||||
|
|
||||||
|
// notePosition wraps an error with position information if it doesn't already
|
||||||
|
// have it.
|
||||||
|
//
|
||||||
|
// notePosition is usually called multiple times as an error goes up the call
|
||||||
|
// stack, so calling notePosition on an existing *wireErr will not modify the
|
||||||
|
// position, as the assumption is that deeper calls have more precise position
|
||||||
|
// information about the source of the error.
|
||||||
|
func notePosition(p token.Position, e error) error {
|
||||||
|
switch e.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case *wireErr:
|
||||||
|
return e
|
||||||
|
default:
|
||||||
|
return &wireErr{error: e, position: p}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notePositionAll wraps a list of errors with the given position.
|
||||||
|
func notePositionAll(p token.Position, errs []error) []error {
|
||||||
|
return mapErrors(errs, func(e error) error {
|
||||||
|
return notePosition(p, e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the error message prefixed by the position if valid.
|
||||||
|
func (w *wireErr) Error() string {
|
||||||
|
if !w.position.IsValid() {
|
||||||
|
return w.error.Error()
|
||||||
|
}
|
||||||
|
return w.position.String() + ": " + w.error.Error()
|
||||||
|
}
|
1247
pkg/build/wire/internal/wire/parse.go
Normal file
1247
pkg/build/wire/internal/wire/parse.go
Normal file
File diff suppressed because it is too large
Load Diff
43
pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/foo.go
vendored
Normal file
43
pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/foo.go
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(inject(Foo{"hello"}).Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fooer interface {
|
||||||
|
Foo() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
f string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Foo) Foo() string {
|
||||||
|
return f.f
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBar(fooer Fooer) *Bar {
|
||||||
|
return &Bar{Name: fooer.Foo()}
|
||||||
|
}
|
29
pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go
vendored
Normal file
29
pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func inject(foo Foo) *Bar {
|
||||||
|
wire.Build(
|
||||||
|
NewBar,
|
||||||
|
wire.Bind(new(Fooer), new(Foo)),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/BindInjectorArg/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/BindInjectorArg/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
hello
|
14
pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go
vendored
Normal file
14
pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func inject(foo Foo) *Bar {
|
||||||
|
bar := NewBar(foo)
|
||||||
|
return bar
|
||||||
|
}
|
43
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/foo.go
vendored
Normal file
43
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/foo.go
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2019 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(inject(&Foo{"hello"}).Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fooer interface {
|
||||||
|
Foo() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
f string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) Foo() string {
|
||||||
|
return f.f
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBar(fooer Fooer) *Bar {
|
||||||
|
return &Bar{Name: fooer.Foo()}
|
||||||
|
}
|
29
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go
vendored
Normal file
29
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2019 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func inject(foo *Foo) *Bar {
|
||||||
|
wire.Build(
|
||||||
|
NewBar,
|
||||||
|
wire.Bind(new(Fooer), new(*Foo)),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
hello
|
14
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go
vendored
Normal file
14
pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func inject(foo *Foo) *Bar {
|
||||||
|
bar := NewBar(foo)
|
||||||
|
return bar
|
||||||
|
}
|
20
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/foo.go
vendored
Normal file
20
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/foo.go
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
w := inject()
|
||||||
|
w.Write([]byte("Hello, World!"))
|
||||||
|
}
|
32
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go
vendored
Normal file
32
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func inject() io.Writer {
|
||||||
|
wire.Build(
|
||||||
|
wire.Value(os.Stdout),
|
||||||
|
wire.Bind(new(io.Writer), new(*os.File)),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
23
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go
vendored
Normal file
23
pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func inject() io.Writer {
|
||||||
|
file := _wireFileValue
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_wireFileValue = os.Stdout
|
||||||
|
)
|
23
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go
vendored
Normal file
23
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build !wireinject
|
||||||
|
|
||||||
|
// Package bar includes both wireinject and non-wireinject variants.
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
|
||||||
|
// Set provides an unfriendly user greeting.
|
||||||
|
var Set = wire.NewSet(wire.Value("Bah humbug! This is the wrong variant!"))
|
22
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go
vendored
Normal file
22
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
|
||||||
|
// Set provides a friendly user greeting.
|
||||||
|
var Set = wire.NewSet(wire.Value("Hello, World!"))
|
21
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/foo.go
vendored
Normal file
21
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/foo.go
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(injectedMessage())
|
||||||
|
}
|
27
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go
vendored
Normal file
27
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"example.com/bar"
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectedMessage() string {
|
||||||
|
wire.Build(bar.Set)
|
||||||
|
return ""
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
18
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go
vendored
Normal file
18
pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func injectedMessage() string {
|
||||||
|
string2 := _wireStringValue
|
||||||
|
return string2
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_wireStringValue = "Hello, World!"
|
||||||
|
)
|
40
pkg/build/wire/internal/wire/testdata/Chain/foo/foo.go
vendored
Normal file
40
pkg/build/wire/internal/wire/testdata/Chain/foo/foo.go
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(injectFooBar())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo int
|
||||||
|
type FooBar int
|
||||||
|
|
||||||
|
var Set = wire.NewSet(
|
||||||
|
provideFoo,
|
||||||
|
provideFooBar)
|
||||||
|
|
||||||
|
func provideFoo() Foo {
|
||||||
|
return 41
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideFooBar(foo Foo) FooBar {
|
||||||
|
return FooBar(foo) + 1
|
||||||
|
}
|
26
pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go
vendored
Normal file
26
pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectFooBar() FooBar {
|
||||||
|
wire.Build(Set)
|
||||||
|
return 0
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/Chain/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/Chain/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/Chain/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/Chain/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
15
pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go
vendored
Normal file
15
pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func injectFooBar() FooBar {
|
||||||
|
foo := provideFoo()
|
||||||
|
fooBar := provideFooBar(foo)
|
||||||
|
return fooBar
|
||||||
|
}
|
46
pkg/build/wire/internal/wire/testdata/Cleanup/foo/foo.go
vendored
Normal file
46
pkg/build/wire/internal/wire/testdata/Cleanup/foo/foo.go
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
bar, cleanup := injectBar()
|
||||||
|
fmt.Println(*bar)
|
||||||
|
cleanup()
|
||||||
|
fmt.Println(*bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo int
|
||||||
|
type Bar int
|
||||||
|
|
||||||
|
func provideFoo() (*Foo, func()) {
|
||||||
|
foo := new(Foo)
|
||||||
|
*foo = 42
|
||||||
|
return foo, func() { *foo = 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideBar(foo *Foo) (*Bar, func()) {
|
||||||
|
bar := new(Bar)
|
||||||
|
*bar = 77
|
||||||
|
return bar, func() {
|
||||||
|
if *foo == 0 {
|
||||||
|
panic("foo cleaned up before bar")
|
||||||
|
}
|
||||||
|
*bar = 0
|
||||||
|
}
|
||||||
|
}
|
26
pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go
vendored
Normal file
26
pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectBar() (*Bar, func()) {
|
||||||
|
wire.Build(provideFoo, provideBar)
|
||||||
|
return nil, nil
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/Cleanup/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/Cleanup/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
2
pkg/build/wire/internal/wire/testdata/Cleanup/want/program_out.txt
vendored
Normal file
2
pkg/build/wire/internal/wire/testdata/Cleanup/want/program_out.txt
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
77
|
||||||
|
0
|
18
pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go
vendored
Normal file
18
pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func injectBar() (*Bar, func()) {
|
||||||
|
foo, cleanup := provideFoo()
|
||||||
|
bar, cleanup2 := provideBar(foo)
|
||||||
|
return bar, func() {
|
||||||
|
cleanup2()
|
||||||
|
cleanup()
|
||||||
|
}
|
||||||
|
}
|
40
pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go
vendored
Normal file
40
pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
// All of the declarations are in one file.
|
||||||
|
// Wire should copy non-injectors over, preserving imports.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(injectedMessage())
|
||||||
|
}
|
||||||
|
|
||||||
|
// provideMessage provides a friendly user greeting.
|
||||||
|
func provideMessage() string {
|
||||||
|
return "Hello, World!"
|
||||||
|
}
|
||||||
|
|
||||||
|
func injectedMessage() string {
|
||||||
|
wire.Build(provideMessage)
|
||||||
|
return ""
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/CopyOtherDecls/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/CopyOtherDecls/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
29
pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go
vendored
Normal file
29
pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Injectors from foo.go:
|
||||||
|
|
||||||
|
func injectedMessage() string {
|
||||||
|
string2 := provideMessage()
|
||||||
|
return string2
|
||||||
|
}
|
||||||
|
|
||||||
|
// foo.go:
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(injectedMessage())
|
||||||
|
}
|
||||||
|
|
||||||
|
// provideMessage provides a friendly user greeting.
|
||||||
|
func provideMessage() string {
|
||||||
|
return "Hello, World!"
|
||||||
|
}
|
37
pkg/build/wire/internal/wire/testdata/Cycle/foo/foo.go
vendored
Normal file
37
pkg/build/wire/internal/wire/testdata/Cycle/foo/foo.go
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(injectedBaz())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo int
|
||||||
|
type Bar int
|
||||||
|
type Baz int
|
||||||
|
|
||||||
|
func provideFoo(_ Baz) Foo {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideBar(_ Foo) Bar {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideBaz(_ Bar) Baz {
|
||||||
|
return 0
|
||||||
|
}
|
26
pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go
vendored
Normal file
26
pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectedBaz() Baz {
|
||||||
|
wire.Build(provideFoo, provideBar, provideBaz)
|
||||||
|
return 0
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/Cycle/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/Cycle/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
5
pkg/build/wire/internal/wire/testdata/Cycle/want/wire_errs.txt
vendored
Normal file
5
pkg/build/wire/internal/wire/testdata/Cycle/want/wire_errs.txt
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
example.com/foo/wire.go:x:y: cycle for example.com/foo.Bar:
|
||||||
|
example.com/foo.Bar (example.com/foo.provideBar) ->
|
||||||
|
example.com/foo.Foo (example.com/foo.provideFoo) ->
|
||||||
|
example.com/foo.Baz (example.com/foo.provideBaz) ->
|
||||||
|
example.com/foo.Bar
|
28
pkg/build/wire/internal/wire/testdata/DocComment/foo/foo.go
vendored
Normal file
28
pkg/build/wire/internal/wire/testdata/DocComment/foo/foo.go
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2020 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Bar struct{}
|
||||||
|
Foo struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello, World")
|
||||||
|
}
|
31
pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go
vendored
Normal file
31
pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2020 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* blockComment returns Foo and has a /*- style doc comment */
|
||||||
|
func blockComment() *Foo {
|
||||||
|
panic(wire.Build(wire.Struct(new(Foo))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// lineComment returns Bar and has a //- style doc comment
|
||||||
|
func lineComment() *Bar {
|
||||||
|
panic(wire.Build(wire.Struct(new(Bar))))
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/DocComment/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/DocComment/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/DocComment/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/DocComment/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello, World
|
21
pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go
vendored
Normal file
21
pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
/* blockComment returns Foo and has a /*- style doc comment */
|
||||||
|
func blockComment() *Foo {
|
||||||
|
foo := &Foo{}
|
||||||
|
return foo
|
||||||
|
}
|
||||||
|
|
||||||
|
// lineComment returns Bar and has a //- style doc comment
|
||||||
|
func lineComment() *Bar {
|
||||||
|
bar := &Bar{}
|
||||||
|
return bar
|
||||||
|
}
|
25
pkg/build/wire/internal/wire/testdata/EmptyVar/foo/foo.go
vendored
Normal file
25
pkg/build/wire/internal/wire/testdata/EmptyVar/foo/foo.go
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(injectedMessage())
|
||||||
|
}
|
||||||
|
|
||||||
|
var myFakeSet struct{}
|
26
pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go
vendored
Normal file
26
pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectedMessage() string {
|
||||||
|
wire.Build(myFakeSet)
|
||||||
|
return ""
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/EmptyVar/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/EmptyVar/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/EmptyVar/want/wire_errs.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/EmptyVar/want/wire_errs.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo/wire.go:x:y: var example.com/foo.myFakeSet struct{} is not a provider or a provider set
|
120
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/foo.go
vendored
Normal file
120
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/foo.go
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// This test demonstrates how to use mocks with wire.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a "real" greeter.
|
||||||
|
// Greet() will include the real current time, so elide it for repeatable
|
||||||
|
// tests.
|
||||||
|
fmt.Printf("Real time greeting: %s [current time elided]\n", initApp().Greet()[0:15])
|
||||||
|
|
||||||
|
// There are two approaches for creating an app with mocks.
|
||||||
|
|
||||||
|
// Approach A: create the mocks manually, and pass them to an injector.
|
||||||
|
// This approach is useful if you need to prime the mocks beforehand.
|
||||||
|
fmt.Println("Approach A")
|
||||||
|
mt := newMockTimer()
|
||||||
|
mockedApp := initMockedAppFromArgs(mt)
|
||||||
|
fmt.Println(mockedApp.Greet()) // prints greeting with time = zero time
|
||||||
|
mt.T = mt.T.AddDate(1999, 0, 0)
|
||||||
|
fmt.Println(mockedApp.Greet()) // prints greeting with time = year 2000
|
||||||
|
|
||||||
|
// Approach B: allow the injector to create the mocks, and return a struct
|
||||||
|
// that includes the resulting app plus the mocks.
|
||||||
|
fmt.Println("Approach B")
|
||||||
|
appWithMocks := initMockedApp()
|
||||||
|
fmt.Println(appWithMocks.app.Greet()) // prints greeting with time = zero time
|
||||||
|
appWithMocks.mt.T = appWithMocks.mt.T.AddDate(999, 0, 0)
|
||||||
|
fmt.Println(appWithMocks.app.Greet()) // prints greeting with time = year 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
// appSet is a provider set for creating a real app.
|
||||||
|
var appSet = wire.NewSet(
|
||||||
|
wire.Struct(new(app), "*"),
|
||||||
|
wire.Struct(new(greeter), "*"),
|
||||||
|
wire.InterfaceValue(new(timer), realTime{}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// appSetWithoutMocks is a provider set for creating an app with mocked
|
||||||
|
// dependencies. The mocked dependencies are omitted and must be provided as
|
||||||
|
// arguments to the injector.
|
||||||
|
// It is used for Approach A.
|
||||||
|
var appSetWithoutMocks = wire.NewSet(
|
||||||
|
wire.Struct(new(app), "*"),
|
||||||
|
wire.Struct(new(greeter), "*"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// mockAppSet is a provider set for creating a mocked app, including the mocked
|
||||||
|
// dependencies.
|
||||||
|
// It is used for Approach B.
|
||||||
|
var mockAppSet = wire.NewSet(
|
||||||
|
wire.Struct(new(app), "*"),
|
||||||
|
wire.Struct(new(greeter), "*"),
|
||||||
|
wire.Struct(new(appWithMocks), "*"),
|
||||||
|
// For each mocked dependency, add a provider and use wire.Bind to bind
|
||||||
|
// the concrete type to the relevant interface.
|
||||||
|
newMockTimer,
|
||||||
|
wire.Bind(new(timer), new(*mockTimer)),
|
||||||
|
)
|
||||||
|
|
||||||
|
type timer interface {
|
||||||
|
Now() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// realTime implements timer with the real time.
|
||||||
|
type realTime struct{}
|
||||||
|
|
||||||
|
func (realTime) Now() time.Time { return time.Now() }
|
||||||
|
|
||||||
|
// mockTimer implements timer using a mocked time.
|
||||||
|
type mockTimer struct {
|
||||||
|
T time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockTimer() *mockTimer { return &mockTimer{} }
|
||||||
|
func (m *mockTimer) Now() time.Time { return m.T }
|
||||||
|
|
||||||
|
// greeter issues greetings with the time provided by T.
|
||||||
|
type greeter struct {
|
||||||
|
T timer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g greeter) Greet() string {
|
||||||
|
return fmt.Sprintf("Good day! It is %v", g.T.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
type app struct {
|
||||||
|
g greeter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a app) Greet() string {
|
||||||
|
return a.g.Greet()
|
||||||
|
}
|
||||||
|
|
||||||
|
// appWithMocks is used for Approach B, to return the app plus its mocked
|
||||||
|
// dependencies.
|
||||||
|
type appWithMocks struct {
|
||||||
|
app app
|
||||||
|
mt *mockTimer
|
||||||
|
}
|
42
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go
vendored
Normal file
42
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// initApp returns a real app.
|
||||||
|
func initApp() *app {
|
||||||
|
wire.Build(appSet)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initMockedAppFromArgs returns an app with mocked dependencies provided via
|
||||||
|
// arguments (Approach A). Note that the argument's type is the interface
|
||||||
|
// type (timer), but the concrete mock type should be passed.
|
||||||
|
func initMockedAppFromArgs(mt timer) *app {
|
||||||
|
wire.Build(appSetWithoutMocks)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initMockedApp returns an app with its mocked dependencies, created
|
||||||
|
// via providers (Approach B).
|
||||||
|
func initMockedApp() *appWithMocks {
|
||||||
|
wire.Build(mockAppSet)
|
||||||
|
return nil
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
7
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/program_out.txt
vendored
Normal file
7
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/program_out.txt
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Real time greeting: Good day! It is [current time elided]
|
||||||
|
Approach A
|
||||||
|
Good day! It is 0001-01-01 00:00:00 +0000 UTC
|
||||||
|
Good day! It is 2000-01-01 00:00:00 +0000 UTC
|
||||||
|
Approach B
|
||||||
|
Good day! It is 0001-01-01 00:00:00 +0000 UTC
|
||||||
|
Good day! It is 1000-01-01 00:00:00 +0000 UTC
|
55
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go
vendored
Normal file
55
pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
// initApp returns a real app.
|
||||||
|
func initApp() *app {
|
||||||
|
mainTimer := _wireRealTimeValue
|
||||||
|
mainGreeter := greeter{
|
||||||
|
T: mainTimer,
|
||||||
|
}
|
||||||
|
mainApp := &app{
|
||||||
|
g: mainGreeter,
|
||||||
|
}
|
||||||
|
return mainApp
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_wireRealTimeValue = realTime{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// initMockedAppFromArgs returns an app with mocked dependencies provided via
|
||||||
|
// arguments (Approach A). Note that the argument's type is the interface
|
||||||
|
// type (timer), but the concrete mock type should be passed.
|
||||||
|
func initMockedAppFromArgs(mt timer) *app {
|
||||||
|
mainGreeter := greeter{
|
||||||
|
T: mt,
|
||||||
|
}
|
||||||
|
mainApp := &app{
|
||||||
|
g: mainGreeter,
|
||||||
|
}
|
||||||
|
return mainApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// initMockedApp returns an app with its mocked dependencies, created
|
||||||
|
// via providers (Approach B).
|
||||||
|
func initMockedApp() *appWithMocks {
|
||||||
|
mainMockTimer := newMockTimer()
|
||||||
|
mainGreeter := greeter{
|
||||||
|
T: mainMockTimer,
|
||||||
|
}
|
||||||
|
mainApp := app{
|
||||||
|
g: mainGreeter,
|
||||||
|
}
|
||||||
|
mainAppWithMocks := &appWithMocks{
|
||||||
|
app: mainApp,
|
||||||
|
mt: mainMockTimer,
|
||||||
|
}
|
||||||
|
return mainAppWithMocks
|
||||||
|
}
|
21
pkg/build/wire/internal/wire/testdata/ExportedValue/bar/bar.go
vendored
Normal file
21
pkg/build/wire/internal/wire/testdata/ExportedValue/bar/bar.go
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
|
||||||
|
var Value = wire.Value(PublicMsg)
|
||||||
|
|
||||||
|
var PublicMsg = "Hello, World!"
|
21
pkg/build/wire/internal/wire/testdata/ExportedValue/foo/foo.go
vendored
Normal file
21
pkg/build/wire/internal/wire/testdata/ExportedValue/foo/foo.go
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(injectedMessage())
|
||||||
|
}
|
27
pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go
vendored
Normal file
27
pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"example.com/bar"
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectedMessage() string {
|
||||||
|
wire.Build(bar.Value)
|
||||||
|
return ""
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/ExportedValue/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/ExportedValue/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/ExportedValue/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/ExportedValue/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
22
pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go
vendored
Normal file
22
pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"example.com/bar"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func injectedMessage() string {
|
||||||
|
string2 := _wireStringValue
|
||||||
|
return string2
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_wireStringValue = bar.PublicMsg
|
||||||
|
)
|
23
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/bar/bar.go
vendored
Normal file
23
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/bar/bar.go
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Value = wire.Value(os.Stdout)
|
21
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/foo.go
vendored
Normal file
21
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/foo.go
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Fprintln(injectedFile(), "Hello, World!")
|
||||||
|
}
|
29
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go
vendored
Normal file
29
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2018 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"example.com/bar"
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectedFile() *os.File {
|
||||||
|
wire.Build(bar.Value)
|
||||||
|
return nil
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
1
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/program_out.txt
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/program_out.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
22
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go
vendored
Normal file
22
pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func injectedFile() *os.File {
|
||||||
|
file := _wireFileValue
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_wireFileValue = os.Stdout
|
||||||
|
)
|
38
pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/foo.go
vendored
Normal file
38
pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/foo.go
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2019 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(injectedBaz())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo int
|
||||||
|
type Baz int
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
Bz Baz
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideFoo(_ Baz) Foo {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideBar(_ Foo) Bar {
|
||||||
|
return Bar{}
|
||||||
|
}
|
26
pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go
vendored
Normal file
26
pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2019 The Wire Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectedBaz() Baz {
|
||||||
|
wire.Build(provideFoo, provideBar, wire.FieldsOf(new(Bar), "Bz"))
|
||||||
|
return 0
|
||||||
|
}
|
1
pkg/build/wire/internal/wire/testdata/FieldsOfCycle/pkg
vendored
Normal file
1
pkg/build/wire/internal/wire/testdata/FieldsOfCycle/pkg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
5
pkg/build/wire/internal/wire/testdata/FieldsOfCycle/want/wire_errs.txt
vendored
Normal file
5
pkg/build/wire/internal/wire/testdata/FieldsOfCycle/want/wire_errs.txt
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
example.com/foo/wire.go:x:y: cycle for example.com/foo.Bar:
|
||||||
|
example.com/foo.Bar (example.com/foo.provideBar) ->
|
||||||
|
example.com/foo.Foo (example.com/foo.provideFoo) ->
|
||||||
|
example.com/foo.Baz (example.com/foo.Bar.Bz) ->
|
||||||
|
example.com/foo.Bar
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user