mirror of https://github.com/grafana/grafana
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 importspull/84430/head^2
parent
36ee1571b6
commit
4d4c06b480
@ -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 |
|
||||||
@ -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> |
||||||
@ -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 |
||||||
@ -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. |
||||||
@ -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> |
||||||
@ -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. |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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() |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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)) |
||||||
|
} |
||||||
|
} |
||||||
Binary file not shown.
@ -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. |
||||||
@ -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. |
||||||
@ -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(/* ... */)) |
||||||
|
} |
||||||
|
``` |
||||||
@ -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 |
||||||
|
) |
||||||
@ -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 |
||||||
@ -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 |
||||||
|
|
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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())) |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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() |
||||||
|
} |
||||||
File diff suppressed because it is too large
Load Diff
@ -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()} |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
hello |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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()} |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
hello |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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!")) |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
Hello, World! |
||||||
@ -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 |
||||||
|
) |
||||||
@ -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!")) |
||||||
@ -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!")) |
||||||
@ -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()) |
||||||
|
} |
||||||
@ -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 "" |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
Hello, World! |
||||||
@ -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!" |
||||||
|
) |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
42 |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
77 |
||||||
|
0 |
||||||
@ -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() |
||||||
|
} |
||||||
|
} |
||||||
@ -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 "" |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
Hello, World! |
||||||
@ -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!" |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -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 |
||||||
@ -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") |
||||||
|
} |
||||||
@ -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)))) |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
Hello, World |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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{} |
||||||
@ -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 "" |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo/wire.go:x:y: var example.com/foo.myFakeSet struct{} is not a provider or a provider set |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -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 |
||||||
@ -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 |
||||||
|
} |
||||||
@ -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!" |
||||||
@ -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()) |
||||||
|
} |
||||||
@ -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 "" |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
Hello, World! |
||||||
@ -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 |
||||||
|
) |
||||||
@ -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) |
||||||
@ -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!") |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -0,0 +1 @@ |
|||||||
|
Hello, World! |
||||||
@ -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 |
||||||
|
) |
||||||
@ -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{} |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
example.com/foo |
||||||
@ -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