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