Chore: Vendor wire into pkg/build (#84637)

* vendor latest wire into pkg/build

* use vendored wire in builds

* fix wire import path

* remove wire from bingo

* also support google/wire import

* make prettier happy

* change package in tess

* add debug walk for drone

* add wire_gen in tests

* remove debug walk

* restore imports
This commit is contained in:
Serge Zaitsev 2024-03-25 11:23:27 +01:00 committed by GitHub
parent 36ee1571b6
commit 4d4c06b480
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
371 changed files with 12711 additions and 29 deletions

View File

@ -1,4 +1,4 @@
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT.
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT.
# All tools are designed to be build inside $GOBIN.
BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
GOPATH ?= $(shell go env GOPATH)
@ -59,9 +59,3 @@ $(SWAGGER): $(BINGO_DIR)/swagger.mod
@echo "(re)installing $(GOBIN)/swagger-v0.30.2"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=swagger.mod -o=$(GOBIN)/swagger-v0.30.2 "github.com/go-swagger/go-swagger/cmd/swagger"
WIRE := $(GOBIN)/wire-v0.6.0
$(WIRE): $(BINGO_DIR)/wire.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
@echo "(re)installing $(GOBIN)/wire-v0.6.0"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=wire.mod -o=$(GOBIN)/wire-v0.6.0 "github.com/google/wire/cmd/wire"

View File

@ -1,4 +1,4 @@
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT.
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT.
# All tools are designed to be build inside $GOBIN.
# Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk.
GOBIN=${GOBIN:=$(go env GOBIN)}
@ -22,5 +22,3 @@ LEFTHOOK="${GOBIN}/lefthook-v1.4.8"
SWAGGER="${GOBIN}/swagger-v0.30.2"
WIRE="${GOBIN}/wire-v0.6.0"

View File

@ -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

View File

@ -117,9 +117,9 @@ gen-feature-toggles:
go test -v ./pkg/services/featuremgmt/...; \
fi
gen-go: $(WIRE)
gen-go:
@echo "generate go files"
$(WIRE) gen -tags $(WIRE_TAGS) ./pkg/server
$(GO) run ./pkg/build/wire/cmd/wire/main.go gen -tags $(WIRE_TAGS) ./pkg/server
fix-cue: $(CUE)
@echo "formatting cue files"

View File

@ -4,6 +4,7 @@ use (
.
./pkg/apimachinery
./pkg/apiserver
./pkg/build/wire
./pkg/promlib
./pkg/util/xorm
)

18
pkg/build/wire/AUTHORS Normal file
View File

@ -0,0 +1,18 @@
# This is the official list of Wire authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as one of
# Organization's name
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
# See CONTRIBUTORS for the meaning of multiple email addresses.
# Please keep the list sorted.
Google LLC
ktr <ktr@syfm.me>
Kumbirai Tanekha <kumbirai.tanekha@gmail.com>
Oleg Kovalov <iamolegkovalov@gmail.com>
Yoichiro Shimizu <budougumi0617@gmail.com>
Zachary Romero <zacromero3@gmail.com>

View File

@ -0,0 +1,10 @@
# Code of Conduct
This project is covered under the [Go Code of Conduct][]. In summary:
- Treat everyone with respect and kindness.
- Be thoughtful in how you communicate.
- Dont be destructive or inflammatory.
- If you encounter an issue, please mail conduct@golang.org.
[Go Code of Conduct]: https://golang.org/conduct

View File

@ -0,0 +1,152 @@
# How to Contribute
We would love to accept your patches and contributions to this project. Here is
how you can help.
## Filing issues
Filing issues is an important way you can contribute to the Wire Project. We
want your feedback on things like bugs, desired API changes, or just anything
that isn't working for you.
### Bugs
If your issue is a bug, open one
[here](https://github.com/google/wire/issues/new). The easiest way to file an
issue with all the right information is to run `go bug`. `go bug` will print out
a handy template of questions and system information that will help us get to
the root of the issue quicker.
### Changes
Unlike the core Go project, we do not have a formal proposal process for
changes. If you have a change you would like to see in Wire, please file an
issue with the necessary details.
### Triaging
The Go Cloud team triages issues at least every two weeks, but usually within
two business days. Bugs or feature requests are either placed into a **Sprint**
milestone which means the issue is intended to be worked on. Issues that we
would like to address but do not have time for are placed into the [Unplanned][]
milestone.
[Unplanned]: https://github.com/google/wire/milestone/1
## Contributing Code
We love accepting contributions! If your change is minor, please feel free
submit a [pull request](https://help.github.com/articles/about-pull-requests/).
If your change is larger, or adds a feature, please file an issue beforehand so
that we can discuss the change. You're welcome to file an implementation pull
request immediately as well, although we generally lean towards discussing the
change and then reviewing the implementation separately.
### Finding something to work on
If you want to write some code, but don't know where to start or what you might
want to do, take a look at our [Unplanned][] milestone. This is where you can
find issues we would like to address but can't currently find time for. See if
any of the latest ones look interesting! If you need help before you can start
work, you can comment on the issue and we will try to help as best we can.
### Contributor License Agreement
Contributions to this project can only be made by those who have signed Google's
Contributor License Agreement. You (or your employer) retain the copyright to
your contribution, this simply gives us permission to use and redistribute your
contributions as part of the project. Head over to
<https://cla.developers.google.com/> to see your current agreements on file or
to sign a new one.
As a personal contributor, you only need to sign the Google CLA once across all
Google projects. If you've already signed the CLA, there is no need to do it
again. If you are submitting code on behalf of your employer, there's
[a separate corporate CLA that your employer manages for you](https://opensource.google.com/docs/cla/#external-contributors).
## Making a pull request
* Follow the normal
[pull request flow](https://help.github.com/articles/creating-a-pull-request/)
* Build your changes using Go 1.11 with Go modules enabled. Wire's continuous
integration uses Go modules in order to ensure
[reproducible builds](https://research.swtch.com/vgo-repro).
* Test your changes using `go test ./...`. Please add tests that show the
change does what it says it does, even if there wasn't a test in the first
place.
* Feel free to make as many commits as you want; we will squash them all into
a single commit before merging your change.
* Check the diffs, write a useful description (including something like
`Fixes #123` if it's fixing a bug) and send the PR out.
* Github will run tests against the PR. This should
happen within 10 minutes or so. If a test fails, go back to the coding stage
and try to fix the test and push the same branch again. You won't need to
make a new pull request, the changes will be rolled directly into the PR you
already opened. Wait for the tests again. There is no need to assign a reviewer
to the PR, the project team will assign someone for review during the
standard [triage](#triaging) process.
## Code review
All submissions, including submissions by project members, require review. It is
almost never the case that a pull request is accepted without some changes
requested, so please do not be offended!
When you have finished making requested changes to your pull request, please
make a comment containing "PTAL" (Please Take Another Look) on your pull
request. GitHub notifications can be noisy, and it is unfortunately easy for
things to be lost in the shuffle.
Once your PR is approved (hooray!) the reviewer will squash your commits into a
single commit, and then merge the commit onto the Wire master branch. Thank you!
## Github code review workflow conventions
(For project members and frequent contributors.)
As a contributor:
- Try hard to make each Pull Request as small and focused as possible. In
particular, this means that if a reviewer asks you to do something that is
beyond the scope of the Pull Request, the best practice is to file another
issue and reference it from the Pull Request rather than just adding more
commits to the existing PR.
- Adding someone as a Reviewer means "please feel free to look and comment";
the review is optional. Choose as many Reviewers as you'd like.
- Adding someone as an Assignee means that the Pull Request should not be
submitted until they approve. If you choose multiple Assignees, wait until
all of them approve. It is fine to ask someone if they are OK with being
removed as an Assignee.
- Note that if you don't select any assignees, ContributeBot will turn all
of your Reviewers into Assignees.
- Make as many commits as you want locally, but try not to push them to Github
until you've addressed comments; this allows the email notification about
the push to be a signal to reviewers that the PR is ready to be looked at
again.
- When there may be confusion about what should happen next for a PR, be
explicit; add a "PTAL" comment if it is ready for review again, or a "Please
hold off on reviewing for now" if you are still working on addressing
comments.
- "Resolve" comments that you are sure you've addressed; let your reviewers
resolve ones that you're not sure about.
- Do not use `git push --force`; this can cause comments from your reviewers
that are associated with a specific commit to be lost. This implies that
once you've sent a Pull Request, you should use `git merge` instead of `git
rebase` to incorporate commits from the master branch.
As a reviewer:
- Be timely in your review process, especially if you are an Assignee.
- Try to use `Start a Review` instead of single comments, to reduce email
spam.
- "Resolve" your own comments if they have been addressed.
- If you want your review to be blocking, and are not currently an Assignee,
add yourself as an Assignee.
When squashing-and-merging:
- Ensure that **all** of the Assignees have approved.
- Do a final review of the one-line PR summary, ensuring that it accurately
describes the change.
- Delete the automatically added commit lines; these are generally not
interesting and make commit history harder to read.

View File

@ -0,0 +1,43 @@
# This is the official list of people who can contribute
# (and typically have contributed) code to the Wire repository.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# Names should be added to this file only after verifying that
# the individual or the individual's organization has agreed to
# the appropriate Contributor License Agreement, found here:
#
# http://code.google.com/legal/individual-cla-v1.0.html
# http://code.google.com/legal/corporate-cla-v1.0.html
#
# The agreement for individuals can be filled out on the web.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file, depending on whether the
# individual or corporate CLA was used.
# Names should be added to this file like so:
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
#
# An entry with multiple email addresses specifies that the
# first address should be used in the submit logs and
# that the other addresses should be recognized as the
# same person when interacting with Git.
# Please keep the list sorted.
Chris Lewis <cflewis@google.com> <cflewis@golang.org> <c@chris.to>
Christina Austin <4240737+clausti@users.noreply.github.com>
Eno Compton <enocom@google.com>
Issac Trotts <issactrotts@google.com> <issac.trotts@gmail.com>
ktr <ktr@syfm.me>
Kumbirai Tanekha <kumbirai.tanekha@gmail.com>
Oleg Kovalov <iamolegkovalov@gmail.com>
Robert van Gent <rvangent@google.com> <vangent@gmail.com>
Ross Light <light@google.com> <ross@zombiezen.com>
Tuo Shan <shantuo@google.com> <sturbo89@gmail.com>
Yoichiro Shimizu <budougumi0617@gmail.com>
Zachary Romero <zacromero3@gmail.com>

202
pkg/build/wire/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

60
pkg/build/wire/README.md Normal file
View File

@ -0,0 +1,60 @@
# Wire: Automated Initialization in Go
[![Build Status](https://github.com/google/wire/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/google/wire/actions)
[![godoc](https://godoc.org/github.com/google/wire?status.svg)][godoc]
[![Coverage](https://codecov.io/gh/google/wire/branch/master/graph/badge.svg)](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

View File

@ -0,0 +1,419 @@
# Wire Tutorial
Let's learn to use Wire by example. The [Wire guide][guide] provides thorough
documentation of the tool's usage. For readers eager to see Wire applied to a
larger server, the [guestbook sample in Go Cloud][guestbook] uses Wire to
initialize its components. Here we are going to build a small greeter program to
understand how to use Wire. The finished product may be found in the same
directory as this README.
[guestbook]: https://github.com/google/go-cloud/tree/master/samples/guestbook
[guide]: https://github.com/google/wire/blob/master/docs/guide.md
## A First Pass of Building the Greeter Program
Let's create a small program that simulates an event with a greeter greeting
guests with a particular message.
To start, we will create three types: 1) a message for a greeter, 2) a greeter
who conveys that message, and 3) an event that starts with the greeter greeting
guests. In this design, we have three `struct` types:
``` go
type Message string
type Greeter struct {
// ... TBD
}
type Event struct {
// ... TBD
}
```
The `Message` type just wraps a string. For now, we will create a simple
initializer that always returns a hard-coded message:
``` go
func NewMessage() Message {
return Message("Hi there!")
}
```
Our `Greeter` will need reference to the `Message`. So let's create an
initializer for our `Greeter` as well.
``` go
func NewGreeter(m Message) Greeter {
return Greeter{Message: m}
}
type Greeter struct {
Message Message // <- adding a Message field
}
```
In the initializer we assign a `Message` field to `Greeter`. Now, we can use the
`Message` when we create a `Greet` method on `Greeter`:
``` go
func (g Greeter) Greet() Message {
return g.Message
}
```
Next, we need our `Event` to have a `Greeter`, so we will create an initializer
for it as well.
``` go
func NewEvent(g Greeter) Event {
return Event{Greeter: g}
}
type Event struct {
Greeter Greeter // <- adding a Greeter field
}
```
Then we add a method to start the `Event`:
``` go
func (e Event) Start() {
msg := e.Greeter.Greet()
fmt.Println(msg)
}
```
The `Start` method holds the core of our small application: it tells the
greeter to issue a greeting and then prints that message to the screen.
Now that we have all the components of our application ready, let's see what it
takes to initialize all the components without using Wire. Our main function
would look like this:
``` go
func main() {
message := NewMessage()
greeter := NewGreeter(message)
event := NewEvent(greeter)
event.Start()
}
```
First we create a message, then we create a greeter with that message, and
finally we create an event with that greeter. With all the initialization done,
we're ready to start our event.
We are using the [dependency injection][di] design principle. In practice, that
means we pass in whatever each component needs. This style of design lends
itself to writing easily tested code and makes it easy to swap out one
dependency with another.
[di]: https://stackoverflow.com/questions/130794/what-is-dependency-injection
## Using Wire to Generate Code
One downside to dependency injection is the need for so many initialization
steps. Let's see how we can use Wire to make the process of initializing our
components smoother.
Let's start by changing our `main` function to look like this:
``` go
func main() {
e := InitializeEvent()
e.Start()
}
```
Next, in a separate file called `wire.go` we will define `InitializeEvent`.
This is where things get interesting:
``` go
// wire.go
func InitializeEvent() Event {
wire.Build(NewEvent, NewGreeter, NewMessage)
return Event{}
}
```
Rather than go through the trouble of initializing each component in turn and
passing it into the next one, we instead have a single call to `wire.Build`
passing in the initializers we want to use. In Wire, initializers are known as
"providers," functions which provide a particular type. We add a zero value for
`Event` as a return value to satisfy the compiler. Note that even if we add
values to `Event`, Wire will ignore them. In fact, the injector's purpose is to
provide information about which providers to use to construct an `Event` and so
we will exclude it from our final binary with a build constraint at the top of
the file:
``` go
//+build wireinject
```
Note, a [build constraint][constraint] requires a blank, trailing line.
In Wire parlance, `InitializeEvent` is an "injector." Now that we have our
injector complete, we are ready to use the `wire` command line tool.
Install the tool with:
``` shell
go install github.com/google/wire/cmd/wire@latest
```
Then in the same directory with the above code, simply run `wire`. Wire will
find the `InitializeEvent` injector and generate a function whose body is
filled out with all the necessary initialization steps. The result will be
written to a file named `wire_gen.go`.
Let's take a look at what Wire did for us:
``` go
// wire_gen.go
func InitializeEvent() Event {
message := NewMessage()
greeter := NewGreeter(message)
event := NewEvent(greeter)
return event
}
```
It looks just like what we wrote above! Now this is a simple example with just
three components, so writing the initializer by hand isn't too painful. Imagine
how useful Wire is for components that are much more complex. When working with
Wire, we will commit both `wire.go` and `wire_gen.go` to source control.
[constraint]: https://godoc.org/go/build#hdr-Build_Constraints
## Making Changes with Wire
To show a small part of how Wire handles more complex setups, let's refactor
our initializer for `Event` to return an error and see what happens.
``` go
func NewEvent(g Greeter) (Event, error) {
if g.Grumpy {
return Event{}, errors.New("could not create event: event greeter is grumpy")
}
return Event{Greeter: g}, nil
}
```
We'll say that sometimes a `Greeter` might be grumpy and so we cannot create
an `Event`. The `NewGreeter` initializer now looks like this:
``` go
func NewGreeter(m Message) Greeter {
var grumpy bool
if time.Now().Unix()%2 == 0 {
grumpy = true
}
return Greeter{Message: m, Grumpy: grumpy}
}
```
We have added a `Grumpy` field to `Greeter` struct and if the invocation time
of the initializer is an even number of seconds since the Unix epoch, we will
create a grumpy greeter instead of a friendly one.
The `Greet` method then becomes:
``` go
func (g Greeter) Greet() Message {
if g.Grumpy {
return Message("Go away!")
}
return g.Message
}
```
Now you see how a grumpy `Greeter` is no good for an `Event`. So `NewEvent` may
fail. Our `main` must now take into account that `InitializeEvent` may in fact
fail:
``` go
func main() {
e, err := InitializeEvent()
if err != nil {
fmt.Printf("failed to create event: %s\n", err)
os.Exit(2)
}
e.Start()
}
```
We also need to update `InitializeEvent` to add an `error` type to the return value:
``` go
// wire.go
func InitializeEvent() (Event, error) {
wire.Build(NewEvent, NewGreeter, NewMessage)
return Event{}, nil
}
```
With the setup complete, we are ready to invoke the `wire` command again. Note,
that after running `wire` once to produce a `wire_gen.go` file, we may also use
`go generate`. Having run the command, our `wire_gen.go` file looks like
this:
``` go
// wire_gen.go
func InitializeEvent() (Event, error) {
message := NewMessage()
greeter := NewGreeter(message)
event, err := NewEvent(greeter)
if err != nil {
return Event{}, err
}
return event, nil
}
```
Wire has detected that the `NewEvent` provider may fail and has done the right
thing inside the generated code: it checks the error and returns early if one
is present.
## Changing the Injector Signature
As another improvement, let's look at how Wire generates code based on the
signature of the injector. Presently, we have hard-coded the message inside
`NewMessage`. In practice, it's much nicer to allow callers to change that
message however they see fit. So let's change `InitializeEvent` to look like
this:
``` go
func InitializeEvent(phrase string) (Event, error) {
wire.Build(NewEvent, NewGreeter, NewMessage)
return Event{}, nil
}
```
Now `InitializeEvent` allows callers to pass in the `phrase` for a `Greeter` to
use. We also add a `phrase` argument to `NewMessage`:
``` go
func NewMessage(phrase string) Message {
return Message(phrase)
}
```
After we run `wire` again, we will see that the tool has generated an
initializer which passes the `phrase` value as a `Message` into `Greeter`.
Neat!
``` go
// wire_gen.go
func InitializeEvent(phrase string) (Event, error) {
message := NewMessage(phrase)
greeter := NewGreeter(message)
event, err := NewEvent(greeter)
if err != nil {
return Event{}, err
}
return event, nil
}
```
Wire inspects the arguments to the injector, sees that we added a string to the
list of arguments (e.g., `phrase`), and likewise sees that among all the
providers, `NewMessage` takes a string, and so it passes `phrase` into
`NewMessage`.
## Catching Mistakes with Helpful Errors
Let's also look at what happens when Wire detects mistakes in our code and see
how Wire's error messages help us correct any problems.
For example, when writing our injector `InitializeEvent`, let's say we forget
to add a provider for `Greeter`. Let's see what happens:
``` go
func InitializeEvent(phrase string) (Event, error) {
wire.Build(NewEvent, NewMessage) // woops! We forgot to add a provider for Greeter
return Event{}, nil
}
```
Running `wire`, we see the following:
``` shell
# wrapping the error across lines for readability
$GOPATH/src/github.com/google/wire/_tutorial/wire.go:24:1:
inject InitializeEvent: no provider found for github.com/google/wire/_tutorial.Greeter
(required by provider of github.com/google/wire/_tutorial.Event)
wire: generate failed
```
Wire is telling us some useful information: it cannot find a provider for
`Greeter`. Note that the error message prints out the full path to the
`Greeter` type. It's also telling us the line number and injector name where
the problem occurred: line 24 inside `InitializeEvent`. In addition, the error
message tells us which provider needs a `Greeter`. It's the `Event` type. Once
we pass in a provider of `Greeter`, the problem will be solved.
Alternatively, what happens if we provide one too many providers to `wire.Build`?
``` go
func NewEventNumber() int {
return 1
}
func InitializeEvent(phrase string) (Event, error) {
// woops! NewEventNumber is unused.
wire.Build(NewEvent, NewGreeter, NewMessage, NewEventNumber)
return Event{}, nil
}
```
Wire helpfully tells us that we have an unused provider:
``` shell
$GOPATH/src/github.com/google/wire/_tutorial/wire.go:24:1:
inject InitializeEvent: unused provider "NewEventNumber"
wire: generate failed
```
Deleting the unused provider from the call to `wire.Build` resolves the error.
## Conclusion
Let's summarize what we have done here. First, we wrote a number of components
with corresponding initializers, or providers. Next, we created an injector
function, specifying which arguments it receives and which types it returns.
Then, we filled in the injector function with a call to `wire.Build` supplying
all necessary providers. Finally, we ran the `wire` command to generate code
that wires up all the different initializers. When we added an argument to the
injector and an error return value, running `wire` again made all the necessary
updates to our generated code.
The example here is small, but it demonstrates some of the power of Wire, and
how it takes much of the pain out of initializing code using dependency
injection. Furthermore, using Wire produced code that looks much like what we
would otherwise write. There are no bespoke types that commit a user to Wire.
Instead it's just generated code. We may do with it what we will. Finally,
another point worth considering is how easy it is to add new dependencies to
our component initialization. As long as we tell Wire how to provide (i.e.,
initialize) a component, we may add that component anywhere in the dependency
graph and Wire will handle the rest.
In closing, it is worth mentioning that Wire supports a number of additional
features not discussed here. Providers may be grouped in [provider sets][sets].
There is support for [binding interfaces][interfaces], [binding
values][values], as well as support for [cleanup functions][cleanup]. See the
[Advanced Features][advanced] section for more.
[advanced]: https://github.com/google/wire/blob/master/docs/guide.md#advanced-features
[cleanup]: https://github.com/google/wire/blob/master/docs/guide.md#cleanup-functions
[interfaces]: https://github.com/google/wire/blob/master/docs/guide.md#binding-interfaces
[sets]: https://github.com/google/wire/blob/master/docs/guide.md#defining-providers
[values]: https://github.com/google/wire/blob/master/docs/guide.md#binding-values

View File

@ -0,0 +1,83 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The greeter binary simulates an event with greeters greeting guests.
package main
import (
"errors"
"fmt"
"os"
"time"
)
// Message is what greeters will use to greet guests.
type Message string
// NewMessage creates a default Message.
func NewMessage(phrase string) Message {
return Message(phrase)
}
// NewGreeter initializes a Greeter. If the current epoch time is an even
// number, NewGreeter will create a grumpy Greeter.
func NewGreeter(m Message) Greeter {
var grumpy bool
if time.Now().Unix()%2 == 0 {
grumpy = true
}
return Greeter{Message: m, Grumpy: grumpy}
}
// Greeter is the type charged with greeting guests.
type Greeter struct {
Grumpy bool
Message Message
}
// Greet produces a greeting for guests.
func (g Greeter) Greet() Message {
if g.Grumpy {
return Message("Go away!")
}
return g.Message
}
// NewEvent creates an event with the specified greeter.
func NewEvent(g Greeter) (Event, error) {
if g.Grumpy {
return Event{}, errors.New("could not create event: event greeter is grumpy")
}
return Event{Greeter: g}, nil
}
// Event is a gathering with greeters.
type Event struct {
Greeter Greeter
}
// Start ensures the event starts with greeting all guests.
func (e Event) Start() {
msg := e.Greeter.Greet()
fmt.Println(msg)
}
func main() {
e, err := InitializeEvent("hi there!")
if err != nil {
fmt.Printf("failed to create event: %s\n", err)
os.Exit(2)
}
e.Start()
}

View File

@ -0,0 +1,28 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build wireinject
// +build wireinject
// The build tag makes sure the stub is not built in the final build.
package main
import "github.com/grafana/grafana/pkg/build/wire"
// InitializeEvent creates an Event. It will error if the Event is staffed with
// a grumpy greeter.
func InitializeEvent(phrase string) (Event, error) {
wire.Build(NewEvent, NewGreeter, NewMessage)
return Event{}, nil
}

View File

@ -0,0 +1,19 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
func InitializeEvent(phrase string) (Event, error) {
message := NewMessage(phrase)
greeter := NewGreeter(message)
event, err := NewEvent(greeter)
if err != nil {
return Event{}, err
}
return event, nil
}

View File

@ -0,0 +1,609 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Wire is a compile-time dependency injection tool.
//
// For an overview, see https://github.com/google/wire/blob/master/README.md
package main
import (
"context"
"flag"
"fmt"
"go/token"
"go/types"
"io/ioutil"
"log"
"os"
"reflect"
"sort"
"strconv"
"strings"
"github.com/google/subcommands"
"github.com/grafana/grafana/pkg/build/wire/internal/wire"
"github.com/pmezard/go-difflib/difflib"
"golang.org/x/tools/go/types/typeutil"
)
func main() {
subcommands.Register(subcommands.CommandsCommand(), "")
subcommands.Register(subcommands.FlagsCommand(), "")
subcommands.Register(subcommands.HelpCommand(), "")
subcommands.Register(&checkCmd{}, "")
subcommands.Register(&diffCmd{}, "")
subcommands.Register(&genCmd{}, "")
subcommands.Register(&showCmd{}, "")
flag.Parse()
// Initialize the default logger to log to stderr.
log.SetFlags(0)
log.SetPrefix("wire: ")
log.SetOutput(os.Stderr)
// TODO(rvangent): Use subcommands's VisitCommands instead of hardcoded map,
// once there is a release that contains it:
// allCmds := map[string]bool{}
// subcommands.DefaultCommander.VisitCommands(func(_ *subcommands.CommandGroup, cmd subcommands.Command) { allCmds[cmd.Name()] = true })
allCmds := map[string]bool{
"commands": true, // builtin
"help": true, // builtin
"flags": true, // builtin
"check": true,
"diff": true,
"gen": true,
"show": true,
}
// Default to running the "gen" command.
if args := flag.Args(); len(args) == 0 || !allCmds[args[0]] {
genCmd := &genCmd{}
os.Exit(int(genCmd.Execute(context.Background(), flag.CommandLine)))
}
os.Exit(int(subcommands.Execute(context.Background())))
}
// packages returns the slice of packages to run wire over based on f.
// It defaults to ".".
func packages(f *flag.FlagSet) []string {
pkgs := f.Args()
if len(pkgs) == 0 {
pkgs = []string{"."}
}
return pkgs
}
// newGenerateOptions returns an initialized wire.GenerateOptions, possibly
// with the Header option set.
func newGenerateOptions(headerFile string) (*wire.GenerateOptions, error) {
opts := new(wire.GenerateOptions)
if headerFile != "" {
var err error
opts.Header, err = ioutil.ReadFile(headerFile)
if err != nil {
return nil, fmt.Errorf("failed to read header file %q: %v", headerFile, err)
}
}
return opts, nil
}
type genCmd struct {
headerFile string
prefixFileName string
tags string
}
func (*genCmd) Name() string { return "gen" }
func (*genCmd) Synopsis() string {
return "generate the wire_gen.go file for each package"
}
func (*genCmd) Usage() string {
return `gen [packages]
Given one or more packages, gen creates the wire_gen.go file for each.
If no packages are listed, it defaults to ".".
`
}
func (cmd *genCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&cmd.headerFile, "header_file", "", "path to file to insert as a header in wire_gen.go")
f.StringVar(&cmd.prefixFileName, "output_file_prefix", "", "string to prepend to output file names.")
f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild")
}
func (cmd *genCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
wd, err := os.Getwd()
if err != nil {
log.Println("failed to get working directory: ", err)
return subcommands.ExitFailure
}
opts, err := newGenerateOptions(cmd.headerFile)
if err != nil {
log.Println(err)
return subcommands.ExitFailure
}
opts.PrefixOutputFile = cmd.prefixFileName
opts.Tags = cmd.tags
outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f), opts)
if len(errs) > 0 {
logErrors(errs)
log.Println("generate failed")
return subcommands.ExitFailure
}
if len(outs) == 0 {
return subcommands.ExitSuccess
}
success := true
for _, out := range outs {
if len(out.Errs) > 0 {
logErrors(out.Errs)
log.Printf("%s: generate failed\n", out.PkgPath)
success = false
}
if len(out.Content) == 0 {
// No Wire output. Maybe errors, maybe no Wire directives.
continue
}
if err := out.Commit(); err == nil {
log.Printf("%s: wrote %s\n", out.PkgPath, out.OutputPath)
} else {
log.Printf("%s: failed to write %s: %v\n", out.PkgPath, out.OutputPath, err)
success = false
}
}
if !success {
log.Println("at least one generate failure")
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}
type diffCmd struct {
headerFile string
tags string
}
func (*diffCmd) Name() string { return "diff" }
func (*diffCmd) Synopsis() string {
return "output a diff between existing wire_gen.go files and what gen would generate"
}
func (*diffCmd) Usage() string {
return `diff [packages]
Given one or more packages, diff generates the content for their wire_gen.go
files and outputs the diff against the existing files.
If no packages are listed, it defaults to ".".
Similar to the diff command, it returns 0 if no diff, 1 if different, 2
plus an error if trouble.
`
}
func (cmd *diffCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&cmd.headerFile, "header_file", "", "path to file to insert as a header in wire_gen.go")
f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild")
}
func (cmd *diffCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
const (
errReturn = subcommands.ExitStatus(2)
diffReturn = subcommands.ExitStatus(1)
)
wd, err := os.Getwd()
if err != nil {
log.Println("failed to get working directory: ", err)
return errReturn
}
opts, err := newGenerateOptions(cmd.headerFile)
if err != nil {
log.Println(err)
return subcommands.ExitFailure
}
opts.Tags = cmd.tags
outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f), opts)
if len(errs) > 0 {
logErrors(errs)
log.Println("generate failed")
return errReturn
}
if len(outs) == 0 {
return subcommands.ExitSuccess
}
success := true
hadDiff := false
for _, out := range outs {
if len(out.Errs) > 0 {
logErrors(out.Errs)
log.Printf("%s: generate failed\n", out.PkgPath)
success = false
}
if len(out.Content) == 0 {
// No Wire output. Maybe errors, maybe no Wire directives.
continue
}
// Assumes the current file is empty if we can't read it.
cur, _ := ioutil.ReadFile(out.OutputPath)
if diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(string(cur)),
B: difflib.SplitLines(string(out.Content)),
}); err == nil {
if diff != "" {
// Print the actual diff to stdout, not stderr.
fmt.Printf("%s: diff from %s:\n%s\n", out.PkgPath, out.OutputPath, diff)
hadDiff = true
}
} else {
log.Printf("%s: failed to diff %s: %v\n", out.PkgPath, out.OutputPath, err)
success = false
}
}
if !success {
log.Println("at least one generate failure")
return errReturn
}
if hadDiff {
return diffReturn
}
return subcommands.ExitSuccess
}
type showCmd struct {
tags string
}
func (*showCmd) Name() string { return "show" }
func (*showCmd) Synopsis() string {
return "describe all top-level provider sets"
}
func (*showCmd) Usage() string {
return `show [packages]
Given one or more packages, show finds all the provider sets declared as
top-level variables and prints what other provider sets they import and what
outputs they can produce, given possible inputs. It also lists any injector
functions defined in the package.
If no packages are listed, it defaults to ".".
`
}
func (cmd *showCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild")
}
func (cmd *showCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
wd, err := os.Getwd()
if err != nil {
log.Println("failed to get working directory: ", err)
return subcommands.ExitFailure
}
info, errs := wire.Load(ctx, wd, os.Environ(), cmd.tags, packages(f))
if info != nil {
keys := make([]wire.ProviderSetID, 0, len(info.Sets))
for k := range info.Sets {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
if keys[i].ImportPath == keys[j].ImportPath {
return keys[i].VarName < keys[j].VarName
}
return keys[i].ImportPath < keys[j].ImportPath
})
for i, k := range keys {
if i > 0 {
fmt.Println()
}
outGroups, imports := gather(info, k)
fmt.Println(k)
for _, imp := range sortSet(imports) {
fmt.Printf("\t%s\n", imp)
}
for i := range outGroups {
fmt.Printf("\tOutputs given %s:\n", outGroups[i].name)
out := make(map[string]token.Pos, outGroups[i].outputs.Len())
outGroups[i].outputs.Iterate(func(t types.Type, v interface{}) {
switch v := v.(type) {
case *wire.Provider:
out[types.TypeString(t, nil)] = v.Pos
case *wire.Value:
out[types.TypeString(t, nil)] = v.Pos
case *wire.Field:
out[types.TypeString(t, nil)] = v.Pos
default:
panic("unreachable")
}
})
for _, t := range sortSet(out) {
fmt.Printf("\t\t%s\n", t)
fmt.Printf("\t\t\tat %v\n", info.Fset.Position(out[t]))
}
}
}
if len(info.Injectors) > 0 {
injectors := append([]*wire.Injector(nil), info.Injectors...)
sort.Slice(injectors, func(i, j int) bool {
if injectors[i].ImportPath == injectors[j].ImportPath {
return injectors[i].FuncName < injectors[j].FuncName
}
return injectors[i].ImportPath < injectors[j].ImportPath
})
fmt.Println("\nInjectors:")
for _, in := range injectors {
fmt.Printf("\t%v\n", in)
}
}
}
if len(errs) > 0 {
logErrors(errs)
log.Println("error loading packages")
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}
type checkCmd struct {
tags string
}
func (*checkCmd) Name() string { return "check" }
func (*checkCmd) Synopsis() string {
return "print any Wire errors found"
}
func (*checkCmd) Usage() string {
return `check [-tags tag,list] [packages]
Given one or more packages, check prints any type-checking or Wire errors
found with top-level variable provider sets or injector functions.
If no packages are listed, it defaults to ".".
`
}
func (cmd *checkCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild")
}
func (cmd *checkCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
wd, err := os.Getwd()
if err != nil {
log.Println("failed to get working directory: ", err)
return subcommands.ExitFailure
}
_, errs := wire.Load(ctx, wd, os.Environ(), cmd.tags, packages(f))
if len(errs) > 0 {
logErrors(errs)
log.Println("error loading packages")
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}
type outGroup struct {
name string
inputs *typeutil.Map // values are not important
outputs *typeutil.Map // values are *wire.Provider, *wire.Value, or *wire.Field
}
// gather flattens a provider set into outputs grouped by the inputs
// required to create them. As it flattens the provider set, it records
// the visited named provider sets as imports.
func gather(info *wire.Info, key wire.ProviderSetID) (_ []outGroup, imports map[string]struct{}) {
set := info.Sets[key]
hash := typeutil.MakeHasher()
// Find imports.
next := []*wire.ProviderSet{info.Sets[key]}
visited := make(map[*wire.ProviderSet]struct{})
imports = make(map[string]struct{})
for len(next) > 0 {
curr := next[len(next)-1]
next = next[:len(next)-1]
if _, found := visited[curr]; found {
continue
}
visited[curr] = struct{}{}
if curr.VarName != "" && !(curr.PkgPath == key.ImportPath && curr.VarName == key.VarName) {
imports[formatProviderSetName(curr.PkgPath, curr.VarName)] = struct{}{}
}
next = append(next, curr.Imports...)
}
// Depth-first search to build groups.
var groups []outGroup
inputVisited := new(typeutil.Map) // values are int, indices into groups or -1 for input.
inputVisited.SetHasher(hash)
var stk []types.Type
for _, k := range set.Outputs() {
// Start a DFS by picking a random unvisited node.
if inputVisited.At(k) == nil {
stk = append(stk, k)
}
// Run DFS
dfs:
for len(stk) > 0 {
curr := stk[len(stk)-1]
stk = stk[:len(stk)-1]
if inputVisited.At(curr) != nil {
continue
}
switch pv := set.For(curr); {
case pv.IsNil():
// This is an input.
inputVisited.Set(curr, -1)
case pv.IsArg():
// This is an injector argument.
inputVisited.Set(curr, -1)
case pv.IsProvider():
// Try to see if any args haven't been visited.
p := pv.Provider()
allPresent := true
for _, arg := range p.Args {
if inputVisited.At(arg.Type) == nil {
allPresent = false
}
}
if !allPresent {
stk = append(stk, curr)
for _, arg := range p.Args {
if inputVisited.At(arg.Type) == nil {
stk = append(stk, arg.Type)
}
}
continue dfs
}
// Build up set of input types, match to a group.
in := new(typeutil.Map)
in.SetHasher(hash)
for _, arg := range p.Args {
i := inputVisited.At(arg.Type).(int)
if i == -1 {
in.Set(arg.Type, true)
} else {
mergeTypeSets(in, groups[i].inputs)
}
}
for i := range groups {
if sameTypeKeys(groups[i].inputs, in) {
groups[i].outputs.Set(curr, p)
inputVisited.Set(curr, i)
continue dfs
}
}
out := new(typeutil.Map)
out.SetHasher(hash)
out.Set(curr, p)
inputVisited.Set(curr, len(groups))
groups = append(groups, outGroup{
inputs: in,
outputs: out,
})
case pv.IsValue():
v := pv.Value()
for i := range groups {
if groups[i].inputs.Len() == 0 {
groups[i].outputs.Set(curr, v)
inputVisited.Set(curr, i)
continue dfs
}
}
in := new(typeutil.Map)
in.SetHasher(hash)
out := new(typeutil.Map)
out.SetHasher(hash)
out.Set(curr, v)
inputVisited.Set(curr, len(groups))
groups = append(groups, outGroup{
inputs: in,
outputs: out,
})
case pv.IsField():
// Try to see if the parent struct hasn't been visited.
f := pv.Field()
if inputVisited.At(f.Parent) == nil {
stk = append(stk, curr, f.Parent)
continue
}
// Build the input map for the parent struct.
in := new(typeutil.Map)
in.SetHasher(hash)
i := inputVisited.At(f.Parent).(int)
if i == -1 {
in.Set(f.Parent, true)
} else {
mergeTypeSets(in, groups[i].inputs)
}
// Group all fields together under the same parent struct.
for i := range groups {
if sameTypeKeys(groups[i].inputs, in) {
groups[i].outputs.Set(curr, f)
inputVisited.Set(curr, i)
continue dfs
}
}
out := new(typeutil.Map)
out.SetHasher(hash)
out.Set(curr, f)
inputVisited.Set(curr, len(groups))
groups = append(groups, outGroup{
inputs: in,
outputs: out,
})
default:
panic("unreachable")
}
}
}
// Name and sort groups.
for i := range groups {
if groups[i].inputs.Len() == 0 {
groups[i].name = "no inputs"
continue
}
instr := make([]string, 0, groups[i].inputs.Len())
groups[i].inputs.Iterate(func(k types.Type, _ interface{}) {
instr = append(instr, types.TypeString(k, nil))
})
sort.Strings(instr)
groups[i].name = strings.Join(instr, ", ")
}
sort.Slice(groups, func(i, j int) bool {
if groups[i].inputs.Len() == groups[j].inputs.Len() {
return groups[i].name < groups[j].name
}
return groups[i].inputs.Len() < groups[j].inputs.Len()
})
return groups, imports
}
func mergeTypeSets(dst, src *typeutil.Map) {
src.Iterate(func(k types.Type, _ interface{}) {
dst.Set(k, true)
})
}
func sameTypeKeys(a, b *typeutil.Map) bool {
if a.Len() != b.Len() {
return false
}
same := true
a.Iterate(func(k types.Type, _ interface{}) {
if b.At(k) == nil {
same = false
}
})
return same
}
func sortSet(set interface{}) []string {
rv := reflect.ValueOf(set)
a := make([]string, 0, rv.Len())
keys := rv.MapKeys()
for _, k := range keys {
a = append(a, k.String())
}
sort.Strings(a)
return a
}
func formatProviderSetName(importPath, varName string) string {
// Since varName is an identifier, it doesn't make sense to quote.
return strconv.Quote(importPath) + "." + varName
}
func logErrors(errs []error) {
for _, err := range errs {
log.Println(strings.Replace(err.Error(), "\n", "\n\t", -1))
}
}

BIN
pkg/build/wire/cmd/wire/wire Executable file

Binary file not shown.

View File

@ -0,0 +1,121 @@
# Best Practices
The following are practices we recommend for using Wire. This list will grow
over time.
## Distinguishing Types
If you need to inject a common type like `string`, create a new string type to
avoid conflicts with other providers. For example:
```go
type MySQLConnectionString string
```
## Options Structs
A provider function that includes many dependencies can pair the function with
an options struct.
```go
type Options struct {
// Messages is the set of recommended greetings.
Messages []Message
// Writer is the location to send greetings. nil goes to stdout.
Writer io.Writer
}
func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
// ...
}
var GreeterSet = wire.NewSet(wire.Struct(new(Options), "*"), NewGreeter)
```
## Provider Sets in Libraries
When creating a provider set for use in a library, the only changes you can make
without breaking compatibility are:
- Change which provider a provider set uses to provide a specific output, as
long as it does not introduce a new input to the provider set. It may remove
inputs. However, note that existing injectors will use the old provider
until they are regenerated.
- Introduce a new output type into the provider set, but only if the type
itself is newly added. If the type is not new, it is possible that some
injector already has the output type included, which would cause a conflict.
All other changes are not safe. This includes:
- Requiring a new input in the provider set.
- Removing an output type from a provider set.
- Adding an existing output type into the provider set.
Instead of making one of these breaking changes, consider adding a new provider
set.
As an example, if you have a provider set like this:
```go
var GreeterSet = wire.NewSet(NewStdoutGreeter)
func DefaultGreeter(ctx context.Context) *Greeter {
// ...
}
func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter {
// ...
}
func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) {
// ...
}
```
You may:
- Use `DefaultGreeter` instead of `NewStdoutGreeter` in `GreeterSet`.
- Create a new type `T` and add a provider for `T` to `GreeterSet`, as long as
`T` is introduced in the same commit/release as the provider is added.
You may not:
- Use `NewGreeter` instead of `NewStdoutGreeter` in `GreeterSet`. This both
adds an input type (`io.Writer`) and requires injectors to return an `error`
where the provider of `*Greeter` did not require this before.
- Remove `NewStdoutGreeter` from `GreeterSet`. Injectors depending on
`*Greeter` will be broken.
- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have
a provider for `io.Writer` which might conflict with this one.
As such, you should pick the output types in a library provider set carefully.
In general, prefer small provider sets in a library. For example, it is common
for a library provider set to contain a single provider function along with a
`wire.Bind` to the interface the return type implements. Avoiding larger
provider sets reduces the likelihood that applications will encounter conflicts.
To illustrate, imagine your library provides a client for a web service. While
it may be tempting to bundle a provider for `*http.Client` in a provider set for
your library's client, doing so would cause conflicts if every library did the
same. Instead, the library's provider set should only include the provider for
the API client, and let `*http.Client` be an input of the provider set.
## Mocking
There are two approaches for creating an injected app with mocked dependencies.
Examples of both approaches are shown
[here](https://github.com/google/wire/tree/master/internal/wire/testdata/ExampleWithMocks/foo).
### Approach A: Pass mocks to the injector
Create a test-only injector that takes all of the mocks as arguments; the
argument types must be the interface types the mocks are mocking. `wire.Build`
can't include providers for the mocked dependencies without creating conflicts,
so if you're using provider set(s) you will need to define one that doesn't
include the mocked types.
### Approach B: Return the mocks from the injector
Create a new struct that includes the app plus all of the dependencies you want
to mock. Create a test-only injector that returns this struct, give it providers
for the concrete mock types, and use `wire.Bind` to tell Wire that the concrete
mock types should be used to fulfill the appropriate interface.

130
pkg/build/wire/docs/faq.md Normal file
View File

@ -0,0 +1,130 @@
# Frequently Asked Questions
## How does Wire relate to other Go dependency injection tools?
Other dependency injection tools for Go like [dig][] or [facebookgo/inject][]
are based on reflection. Wire runs as a code generator, which means that the
injector works without making calls to a runtime library. This enables easier
introspection of initialization and correct cross-references for tooling like
[guru][].
[dig]: https://github.com/uber-go/dig
[facebookgo/inject]: https://github.com/facebookgo/inject
[guru]: https://golang.org/s/using-guru
## How does Wire relate to other non-Go dependency injection tools (like Dagger 2)?
Wire's approach was inspired by [Dagger 2][]. However, it is not the aim of Wire
to emulate dependency injection tools from other languages: the design space and
requirements are quite different. For example, the Go compiler does not support
anything like Java's annotation processing mechanisms. The difference in
languages and their idioms necessarily requires different approaches in
primitives and API.
[Dagger 2]: https://google.github.io/dagger/
## Why use pseudo-functions to create provider sets or injectors?
In the early prototypes, Wire directives were specially formatted comments. This
seemed appealing at first glance as this meant no compile-time or runtime
impact. However, this unstructured approach becomes opaque to other tooling not
written for Wire. Tools like [`gorename`][] or [guru][] would not be able to
recognize references to the identifiers existing in comments without being
specially modified to understand Wire's comment format. By moving the references
into no-op function calls, Wire interoperates seamlessly with other Go tooling.
[`gorename`]: https://godoc.org/golang.org/x/tools/cmd/gorename
## What if my dependency graph has two dependencies of the same type?
This most frequently appears with common types like `string`. An example of this
problem would be:
```go
type Foo struct { /* ... */ }
type Bar struct { /* ... */ }
func newFoo1() *Foo { /* ... */ }
func newFoo2() *Foo { /* ... */ }
func newBar(foo1 *Foo, foo2 *Foo) *Bar { /* ... */ }
func inject() *Bar {
// ERROR! Multiple providers for *Foo.
wire.Build(newFoo1, newFoo2, newBar)
return nil
}
```
Wire does not allow multiple providers for one type to exist in the transitive
closure of the providers presented to `wire.Build`, as this is usually a
mistake. For legitimate cases where you need multiple dependencies of the same
type, you need to invent a new type to call this other dependency. For example,
you can name OAuth credentials after the service they connect to. Once you have
a suitable different type, you can wrap and unwrap the type when plumbing it
through Wire. Continuing our above example:
```go
type OtherFoo Foo
func newOtherFoo() *OtherFoo {
// Call the original provider...
foo := newFoo2()
// ...then convert it to the new type.
return (*OtherFoo)(foo)
}
func provideBar(foo1 *Foo, otherFoo *OtherFoo) *Bar {
// Convert the new type into the unwrapped type...
foo2 := (*Foo)(otherFoo)
// ...then use it to call the original provider.
return newBar(foo1, foo2)
}
func inject() *Bar {
wire.Build(newFoo1, newOtherFoo, provideBar)
return nil
}
```
## Why does Wire forbid including the same provider multiple times?
Wire forbids this to remain consistent with the principle that specifying
multiple providers for the same type is an error. On the surface, Wire could
permit duplication, but this would introduce a few unintended consequences:
- Wire would have to specify what kinds of duplicates are permissible: are two
`wire.Value` calls ever considered to be the "same"?
- If a provider set changes the function it uses to provide a type, then this
could break an application, since it may introduce a new conflict between
another provider set that was specifying the "same" provider.
As such, we decided that the simpler behavior would be for this case to be an
error, knowing we can always relax this restriction later. The user can always
create a new provider set that does not have the conflicting type. A [proposed
subtract command][] would automate the toil in this process.
[proposed subtract command]: https://github.com/google/wire/issues/8
## Why does Wire require explicitly declare that a type provides an interface type?
The reason the binding is explicit is to avoid scenarios where adding a new type
to the provider graph that implements the same interface causes the graph to
break, because that can be surprising. While this does result in more typing,
the end-effect is that the developer's intent is more explicit in the code,
which we felt was most consistent with the Go philosophy.
There is an [open issue](https://github.com/google/wire/issues/242) to consider
improving this.
## Should I use Wire for small applications?
Probably not. Wire is designed to automate more intricate setup code found in
larger applications. For small applications, hand-wiring dependencies is
simpler.
## Who is using Wire?
Wire is still fairly new and doesn't have a large user base yet. However, we
have heard a lot of interest from Go users wanting to simplify their
applications. If your project or company uses Wire, please let us know by either
emailing us or sending a pull request amending this section.

View File

@ -0,0 +1,465 @@
# Wire User Guide
## Basics
Wire has two core concepts: providers and injectors.
### Defining Providers
The primary mechanism in Wire is the **provider**: a function that can produce a
value. These functions are ordinary Go code.
```go
package foobarbaz
type Foo struct {
X int
}
// ProvideFoo returns a Foo.
func ProvideFoo() Foo {
return Foo{X: 42}
}
```
Provider functions must be exported in order to be used from other packages,
just like ordinary functions.
Providers can specify dependencies with parameters:
```go
package foobarbaz
// ...
type Bar struct {
X int
}
// ProvideBar returns a Bar: a negative Foo.
func ProvideBar(foo Foo) Bar {
return Bar{X: -foo.X}
}
```
Providers can also return errors:
```go
package foobarbaz
import (
"context"
"errors"
)
// ...
type Baz struct {
X int
}
// ProvideBaz returns a value if Bar is not zero.
func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
if bar.X == 0 {
return Baz{}, errors.New("cannot provide baz when bar is zero")
}
return Baz{X: bar.X}, nil
}
```
Providers can be grouped into **provider sets**. This is useful if several
providers will frequently be used together. To add these providers to a new set
called `SuperSet`, use the `wire.NewSet` function:
```go
package foobarbaz
import (
// ...
"github.com/google/wire"
)
// ...
var SuperSet = wire.NewSet(ProvideFoo, ProvideBar, ProvideBaz)
```
You can also add other provider sets into a provider set.
```go
package foobarbaz
import (
// ...
"example.com/some/other/pkg"
)
// ...
var MegaSet = wire.NewSet(SuperSet, pkg.OtherSet)
```
### Injectors
An application wires up these providers with an **injector**: a function that
calls providers in dependency order. With Wire, you write the injector's
signature, then Wire generates the function's body.
An injector is declared by writing a function declaration whose body is a call
to `wire.Build`. The return values don't matter as long as they are of the
correct type. The values themselves will be ignored in the generated code. Let's
say that the above providers were defined in a package called
`example.com/foobarbaz`. The following would declare an injector to obtain a
`Baz`:
```go
// +build wireinject
// The build tag makes sure the stub is not built in the final build.
package main
import (
"context"
"github.com/google/wire"
"example.com/foobarbaz"
)
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
wire.Build(foobarbaz.MegaSet)
return foobarbaz.Baz{}, nil
}
```
Like providers, injectors can be parameterized on inputs (which then get sent to
providers) and can return errors. Arguments to `wire.Build` are the same as
`wire.NewSet`: they form a provider set. This is the provider set that gets used
during code generation for that injector.
Any non-injector declarations found in a file with injectors will be copied into
the generated file.
You can generate the injector by invoking Wire in the package directory:
```shell
wire
```
Wire will produce an implementation of the injector in a file called
`wire_gen.go` that looks something like this:
```go
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//+build !wireinject
package main
import (
"example.com/foobarbaz"
)
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
foo := foobarbaz.ProvideFoo()
bar := foobarbaz.ProvideBar(foo)
baz, err := foobarbaz.ProvideBaz(ctx, bar)
if err != nil {
return foobarbaz.Baz{}, err
}
return baz, nil
}
```
As you can see, the output is very close to what a developer would write
themselves. Further, there is little dependency on Wire at runtime: all of the
written code is just normal Go code, and can be used without Wire.
Once `wire_gen.go` is created, you can regenerate it by running [`go generate`].
[`go generate`]: https://blog.golang.org/generate
## Advanced Features
The following features all build on top of the concepts of providers and
injectors.
### Binding Interfaces
Frequently, dependency injection is used to bind a concrete implementation for
an interface. Wire matches inputs to outputs via [type identity][], so the
inclination might be to create a provider that returns an interface type.
However, this would not be idiomatic, since the Go best practice is to
[return concrete types][]. Instead, you can declare an interface binding in a
provider set:
```go
type Fooer interface {
Foo() string
}
type MyFooer string
func (b *MyFooer) Foo() string {
return string(*b)
}
func provideMyFooer() *MyFooer {
b := new(MyFooer)
*b = "Hello, World!"
return b
}
type Bar string
func provideBar(f Fooer) string {
// f will be a *MyFooer.
return f.Foo()
}
var Set = wire.NewSet(
provideMyFooer,
wire.Bind(new(Fooer), new(*MyFooer)),
provideBar)
```
The first argument to `wire.Bind` is a pointer to a value of the desired
interface type and the second argument is a pointer to a value of the type that
implements the interface. Any set that includes an interface binding must also
have a provider in the same set that provides the concrete type.
[type identity]: https://golang.org/ref/spec#Type_identity
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
### Struct Providers
Structs can be constructed using provided types. Use the `wire.Struct` function
to construct a struct type and tell the injector which field(s) should be injected.
The injector will fill in each field using the provider for the field's type.
For the resulting struct type `S`, `wire.Struct` provides both `S` and `*S`. For
example, given the following providers:
```go
type Foo int
type Bar int
func ProvideFoo() Foo {/* ... */}
func ProvideBar() Bar {/* ... */}
type FooBar struct {
MyFoo Foo
MyBar Bar
}
var Set = wire.NewSet(
ProvideFoo,
ProvideBar,
wire.Struct(new(FooBar), "MyFoo", "MyBar"))
```
A generated injector for `FooBar` would look like this:
```go
func injectFooBar() FooBar {
foo := ProvideFoo()
bar := ProvideBar()
fooBar := FooBar{
MyFoo: foo,
MyBar: bar,
}
return fooBar
}
```
The first argument to `wire.Struct` is a pointer to the desired struct type and
the subsequent arguments are the names of fields to be injected. A special
string `"*"` can be used as a shortcut to tell the injector to inject all
fields. So `wire.Struct(new(FooBar), "*")` produces the same result as above.
For the above example, you can specify only injecting `"MyFoo"` by changing the
`Set` to:
```go
var Set = wire.NewSet(
ProvideFoo,
wire.Struct(new(FooBar), "MyFoo"))
```
Then the generated injector for `FooBar` would look like this:
```go
func injectFooBar() FooBar {
foo := ProvideFoo()
fooBar := FooBar{
MyFoo: foo,
}
return fooBar
}
```
If the injector returned a `*FooBar` instead of a `FooBar`, the generated injector
would look like this:
```go
func injectFooBar() *FooBar {
foo := ProvideFoo()
fooBar := &FooBar{
MyFoo: foo,
}
return fooBar
}
```
It is sometimes useful to prevent certain fields from being filled in by the
injector, especially when passing `*` to `wire.Struct`. You can tag a field with
`` `wire:"-"` `` to have Wire ignore such fields. For example:
```go
type Foo struct {
mu sync.Mutex `wire:"-"`
Bar Bar
}
```
When you provide the `Foo` type using `wire.Struct(new(Foo), "*")`, Wire will
automatically omit the `mu` field. Additionally, it is an error to explicitly
specify a prevented field as in `wire.Struct(new(Foo), "mu")`.
### Binding Values
Occasionally, it is useful to bind a basic value (usually `nil`) to a type.
Instead of having injectors depend on a throwaway provider function, you can add
a value expression to a provider set.
```go
type Foo struct {
X int
}
func injectFoo() Foo {
wire.Build(wire.Value(Foo{X: 42}))
return Foo{}
}
```
The generated injector would look like this:
```go
func injectFoo() Foo {
foo := _wireFooValue
return foo
}
var (
_wireFooValue = Foo{X: 42}
)
```
It's important to note that the expression will be copied to the injector's
package; references to variables will be evaluated during the injector package's
initialization. Wire will emit an error if the expression calls any functions or
receives from any channels.
For interface values, use `InterfaceValue`:
```go
func injectReader() io.Reader {
wire.Build(wire.InterfaceValue(new(io.Reader), os.Stdin))
return nil
}
```
### Use Fields of a Struct as Providers
Sometimes the providers the user wants are some fields of a struct. If you find
yourself writing a provider like `getS` in the example below to promote struct
fields into provided types:
```go
type Foo struct {
S string
N int
F float64
}
func getS(foo Foo) string {
// Bad! Use wire.FieldsOf instead.
return foo.S
}
func provideFoo() Foo {
return Foo{ S: "Hello, World!", N: 1, F: 3.14 }
}
func injectedMessage() string {
wire.Build(
provideFoo,
getS)
return ""
}
```
You can instead use `wire.FieldsOf` to use those fields directly without writing
`getS`:
```go
func injectedMessage() string {
wire.Build(
provideFoo,
wire.FieldsOf(new(Foo), "S"))
return ""
}
```
The generated injector would look like this:
```go
func injectedMessage() string {
foo := provideFoo()
string2 := foo.S
return string2
}
```
You can add as many field names to a `wire.FieldsOf` function as you like.
For a given field type `T`, `FieldsOf` provides at least `T`; if the struct
argument is a pointer to a struct, then `FieldsOf` also provides `*T`.
### Cleanup functions
If a provider creates a value that needs to be cleaned up (e.g. closing a file),
then it can return a closure to clean up the resource. The injector will use
this to either return an aggregated cleanup function to the caller or to clean
up the resource if a provider called later in the injector's implementation
returns an error.
```go
func provideFile(log Logger, path Path) (*os.File, func(), error) {
f, err := os.Open(string(path))
if err != nil {
return nil, nil, err
}
cleanup := func() {
if err := f.Close(); err != nil {
log.Log(err)
}
}
return f, cleanup, nil
}
```
A cleanup function is guaranteed to be called before the cleanup function of any
of the provider's inputs and must have the signature `func()`.
### Alternate Injector Syntax
If you grow weary of writing `return foobarbaz.Foo{}, nil` at the end of your
injector function declaration, you can instead write it more concisely with a
`panic`:
```go
func injectFoo() Foo {
panic(wire.Build(/* ... */))
}
```

10
pkg/build/wire/go.mod Normal file
View File

@ -0,0 +1,10 @@
module github.com/grafana/grafana/pkg/build/wire
go 1.12
require (
github.com/google/go-cmp v0.6.0
github.com/google/subcommands v1.2.0
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
golang.org/x/tools v0.17.0
)

View File

@ -1,14 +1,7 @@
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@ -19,7 +12,6 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@ -31,6 +23,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -55,8 +48,6 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s=
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=

View File

@ -0,0 +1,5 @@
github.com/google/subcommands
github.com/google/wire
github.com/pmezard/go-difflib
golang.org/x/mod
golang.org/x/tools

View File

@ -0,0 +1,89 @@
#!/usr/bin/env bash
# Copyright 2019 The Wire Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script checks to see if there are any incompatible API changes on the
# current branch relative to the upstream branch.
# It fails if it finds any, unless there is a commit with BREAKING_CHANGE_OK
# in the first line of the commit message.
# This script expects:
# a) to be run at the root of the repository
# b) HEAD is pointing to a commit that merges between the pull request and the
# upstream branch (GITHUB_BASE_REF).
set -euo pipefail
UPSTREAM_BRANCH="${GITHUB_BASE_REF:-master}"
echo "Checking for incompatible API changes relative to ${UPSTREAM_BRANCH}..."
MASTER_CLONE_DIR="$(mktemp -d)"
PKGINFO_BRANCH=$(mktemp)
PKGINFO_MASTER=$(mktemp)
function cleanup() {
rm -rf "$MASTER_CLONE_DIR"
rm -f "$PKGINFO_BRANCH"
rm -f "$PKGINFO_MASTER"
}
trap cleanup EXIT
# Install apidiff.
go install golang.org/x/exp/cmd/apidiff@latest
git clone -b "$UPSTREAM_BRANCH" . "$MASTER_CLONE_DIR" &> /dev/null
incompatible_change_pkgs=()
PKGS=$(cd "$MASTER_CLONE_DIR"; go list ./... | grep -v test | grep -v internal)
for pkg in $PKGS; do
echo " Testing ${pkg}..."
# Compute export data for the current branch.
package_deleted=0
apidiff -w "$PKGINFO_BRANCH" "$pkg" || package_deleted=1
if [[ $package_deleted -eq 1 ]]; then
echo " Package ${pkg} was deleted! Recording as an incompatible change.";
incompatible_change_pkgs+=(${pkg});
continue;
fi
# Compute export data for master@HEAD.
(cd "$MASTER_CLONE_DIR"; apidiff -w "$PKGINFO_MASTER" "$pkg")
# Print all changes for posterity.
apidiff "$PKGINFO_MASTER" "$PKGINFO_BRANCH"
# Note if there's an incompatible change.
ic=$(apidiff -incompatible "$PKGINFO_MASTER" "$PKGINFO_BRANCH")
if [ ! -z "$ic" ]; then
incompatible_change_pkgs+=("$pkg");
fi
done
if [ ${#incompatible_change_pkgs[@]} -eq 0 ]; then
# No incompatible changes, we are good.
echo "OK: No incompatible changes found."
exit 0;
fi
echo "Found breaking API change(s) in: ${incompatible_change_pkgs[*]}."
# Found incompatible changes; see if they were declared as OK via a commit.
if git cherry -v master | grep -q "BREAKING_CHANGE_OK"; then
echo "Allowing them due to a commit message with BREAKING_CHANGE_OK.";
exit 0;
fi
echo "FAIL. If this is expected and OK, you can pass this check by adding a commit with BREAKING_CHANGE_OK in the first line of the message."
exit 1

View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Copyright 2019 The Wire Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
# To run this script manually to update alldeps:
#
# $ internal/listdeps.sh > internal/alldeps
#
# Important note: there are changes in module tooling behavior between go 1.11
# and go 1.12; please make sure to use the same version of Go as used by Github
# Actions (see .github/workflows/tests.yml) when updating the alldeps file.
go list -deps -f '{{with .Module}}{{.Path}}{{end}}' ./... | sort | uniq

View File

@ -0,0 +1,80 @@
#!/usr/bin/env bash
# Copyright 2019 The Wire Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# https://coderwall.com/p/fkfaqq/safer-bash-scripts-with-set-euxo-pipefail
set -euo pipefail
if [[ $# -gt 0 ]]; then
echo "usage: runtests.sh" 1>&2
exit 64
fi
# Run Go tests. Only do coverage for the Linux build
# because it is slow, and codecov will only save the last one anyway.
result=0
if [[ "${RUNNER_OS:-}" == "Linux" ]]; then
echo "Running Go tests (with coverage)..."
go test -mod=readonly -race -coverpkg=./... -coverprofile=coverage.out ./... || result=1
if [ -f coverage.out ] && [ $result -eq 0 ]; then
bash <(curl -s https://codecov.io/bash)
fi
else
echo "Running Go tests..."
go test -mod=readonly -race ./... || result=1
fi
# No need to run other checks on OSs other than linux.
# We default RUNNER_OS to "Linux" so that we don't abort here when run locally.
if [[ "${RUNNER_OS:-Linux}" != "Linux" ]]; then
exit $result
fi
echo
echo "Ensuring .go files are formatted with gofmt -s..."
mapfile -t go_files < <(find . -name '*.go' -type f | grep -v testdata)
DIFF="$(gofmt -s -d "${go_files[@]}")"
if [ -n "$DIFF" ]; then
echo "FAIL: please run gofmt -s and commit the result"
echo "$DIFF";
result=1;
else
echo "OK"
fi;
# Ensure that the code has no extra dependencies (including transitive
# dependencies) that we're not already aware of by comparing with
# ./internal/alldeps
#
# Whenever project dependencies change, rerun ./internal/listdeps.sh
echo
echo "Ensuring that there are no dependencies not listed in ./internal/alldeps..."
(./internal/listdeps.sh | diff ./internal/alldeps - && echo "OK") || {
echo "FAIL: dependencies changed; run: internal/listdeps.sh > internal/alldeps"
# Module behavior may differ across versions.
echo "using the latest go version."
result=1
}
# For pull requests, check if there are undeclared incompatible API changes.
# Skip this if we're already going to fail since it is expensive.
# CURRENTLY BROKEN
# if [[ ${result} -eq 0 ]] && [[ ! -z "${GITHUB_HEAD_REF:-x}" ]]; then
# echo
# ./internal/check_api_change.sh || result=1;
# fi
exit $result

View File

@ -0,0 +1,521 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package wire
import (
"errors"
"fmt"
"go/ast"
"go/token"
"go/types"
"sort"
"strings"
"golang.org/x/tools/go/types/typeutil"
)
type callKind int
const (
funcProviderCall callKind = iota
structProvider
valueExpr
selectorExpr
)
// A call represents a step of an injector function. It may be either a
// function call or a composite struct literal, depending on the value
// of kind.
type call struct {
// kind indicates the code pattern to use.
kind callKind
// out is the type this step produces.
out types.Type
// pkg and name identify one of the following:
// 1) the provider to call for kind == funcProviderCall;
// 2) the type to construct for kind == structProvider;
// 3) the name to select for kind == selectorExpr.
pkg *types.Package
name string
// args is a list of arguments to call the provider with. Each element is:
// a) one of the givens (args[i] < len(given)),
// b) the result of a previous provider call (args[i] >= len(given))
//
// This will be nil for kind == valueExpr.
//
// If kind == selectorExpr, then the length of this slice will be 1 and the
// "argument" will be the value to access fields from.
args []int
// varargs is true if the provider function is variadic.
varargs bool
// fieldNames maps the arguments to struct field names.
// This will only be set if kind == structProvider.
fieldNames []string
// ins is the list of types this call receives as arguments.
// This will be nil for kind == valueExpr.
ins []types.Type
// The following are only set for kind == funcProviderCall:
// hasCleanup is true if the provider call returns a cleanup function.
hasCleanup bool
// hasErr is true if the provider call returns an error.
hasErr bool
// The following are only set for kind == valueExpr:
valueExpr ast.Expr
valueTypeInfo *types.Info
// The following are only set for kind == selectorExpr:
ptrToField bool
}
// solve finds the sequence of calls required to produce an output type
// with an optional set of provided inputs.
func solve(fset *token.FileSet, out types.Type, given *types.Tuple, set *ProviderSet) ([]call, []error) {
ec := new(errorCollector)
// Start building the mapping of type to local variable of the given type.
// The first len(given) local variables are the given types.
index := new(typeutil.Map)
for i := 0; i < given.Len(); i++ {
index.Set(given.At(i).Type(), i)
}
// Topological sort of the directed graph defined by the providers
// using a depth-first search using a stack. Provider set graphs are
// guaranteed to be acyclic. An index value of errAbort indicates that
// the type was visited, but failed due to an error added to ec.
errAbort := errors.New("failed to visit")
var used []*providerSetSrc
var calls []call
type frame struct {
t types.Type
from types.Type
up *frame
}
stk := []frame{{t: out}}
dfs:
for len(stk) > 0 {
curr := stk[len(stk)-1]
stk = stk[:len(stk)-1]
if index.At(curr.t) != nil {
continue
}
pv := set.For(curr.t)
if pv.IsNil() {
if curr.from == nil {
ec.add(fmt.Errorf("no provider found for %s, output of injector", types.TypeString(curr.t, nil)))
index.Set(curr.t, errAbort)
continue
}
sb := new(strings.Builder)
fmt.Fprintf(sb, "no provider found for %s", types.TypeString(curr.t, nil))
for f := curr.up; f != nil; f = f.up {
fmt.Fprintf(sb, "\nneeded by %s in %s", types.TypeString(f.t, nil), set.srcMap.At(f.t).(*providerSetSrc).description(fset, f.t))
}
ec.add(errors.New(sb.String()))
index.Set(curr.t, errAbort)
continue
}
src := set.srcMap.At(curr.t).(*providerSetSrc)
used = append(used, src)
if concrete := pv.Type(); !types.Identical(concrete, curr.t) {
// Interface binding does not create a call.
i := index.At(concrete)
if i == nil {
stk = append(stk, curr, frame{t: concrete, from: curr.t, up: &curr})
continue
}
index.Set(curr.t, i)
continue
}
switch pv := set.For(curr.t); {
case pv.IsArg():
// Continue, already added to stk.
case pv.IsProvider():
p := pv.Provider()
// Ensure that all argument types have been visited. If not, push them
// on the stack in reverse order so that calls are added in argument
// order.
visitedArgs := true
for i := len(p.Args) - 1; i >= 0; i-- {
a := p.Args[i]
if index.At(a.Type) == nil {
if visitedArgs {
// Make sure to re-visit this type after visiting all arguments.
stk = append(stk, curr)
visitedArgs = false
}
stk = append(stk, frame{t: a.Type, from: curr.t, up: &curr})
}
}
if !visitedArgs {
continue
}
args := make([]int, len(p.Args))
ins := make([]types.Type, len(p.Args))
for i := range p.Args {
ins[i] = p.Args[i].Type
v := index.At(p.Args[i].Type)
if v == errAbort {
index.Set(curr.t, errAbort)
continue dfs
}
args[i] = v.(int)
}
index.Set(curr.t, given.Len()+len(calls))
kind := funcProviderCall
fieldNames := []string(nil)
if p.IsStruct {
kind = structProvider
for _, arg := range p.Args {
fieldNames = append(fieldNames, arg.FieldName)
}
}
calls = append(calls, call{
kind: kind,
pkg: p.Pkg,
name: p.Name,
args: args,
varargs: p.Varargs,
fieldNames: fieldNames,
ins: ins,
out: curr.t,
hasCleanup: p.HasCleanup,
hasErr: p.HasErr,
})
case pv.IsValue():
v := pv.Value()
index.Set(curr.t, given.Len()+len(calls))
calls = append(calls, call{
kind: valueExpr,
out: curr.t,
valueExpr: v.expr,
valueTypeInfo: v.info,
})
case pv.IsField():
f := pv.Field()
if index.At(f.Parent) == nil {
// Fields have one dependency which is the parent struct. Make
// sure to visit it first if it is not already visited.
stk = append(stk, curr, frame{t: f.Parent, from: curr.t, up: &curr})
continue
}
index.Set(curr.t, given.Len()+len(calls))
v := index.At(f.Parent)
if v == errAbort {
index.Set(curr.t, errAbort)
continue dfs
}
// Use args[0] to store the position of the parent struct.
args := []int{v.(int)}
// If f.Out has 2 elements and curr.t is the 2nd one, then the call must
// provide a pointer to the field.
ptrToField := len(f.Out) == 2 && types.Identical(curr.t, f.Out[1])
calls = append(calls, call{
kind: selectorExpr,
pkg: f.Pkg,
name: f.Name,
out: curr.t,
args: args,
ptrToField: ptrToField,
})
default:
panic("unknown return value from ProviderSet.For")
}
}
if len(ec.errors) > 0 {
return nil, ec.errors
}
if errs := verifyArgsUsed(set, used); len(errs) > 0 {
return nil, errs
}
return calls, nil
}
// verifyArgsUsed ensures that all of the arguments in set were used during solve.
func verifyArgsUsed(set *ProviderSet, used []*providerSetSrc) []error {
var errs []error
for _, imp := range set.Imports {
found := false
for _, u := range used {
if u.Import == imp {
found = true
break
}
}
if !found {
if imp.VarName == "" {
errs = append(errs, errors.New("unused provider set"))
} else {
errs = append(errs, fmt.Errorf("unused provider set %q", imp.VarName))
}
}
}
for _, p := range set.Providers {
found := false
for _, u := range used {
if u.Provider == p {
found = true
break
}
}
if !found {
errs = append(errs, fmt.Errorf("unused provider %q", p.Pkg.Name()+"."+p.Name))
}
}
for _, v := range set.Values {
found := false
for _, u := range used {
if u.Value == v {
found = true
break
}
}
if !found {
errs = append(errs, fmt.Errorf("unused value of type %s", types.TypeString(v.Out, nil)))
}
}
for _, b := range set.Bindings {
found := false
for _, u := range used {
if u.Binding == b {
found = true
break
}
}
if !found {
errs = append(errs, fmt.Errorf("unused interface binding to type %s", types.TypeString(b.Iface, nil)))
}
}
for _, f := range set.Fields {
found := false
for _, u := range used {
if u.Field == f {
found = true
break
}
}
if !found {
errs = append(errs, fmt.Errorf("unused field %q.%s", f.Parent, f.Name))
}
}
return errs
}
// buildProviderMap creates the providerMap and srcMap fields for a given
// provider set. The given provider set's providerMap and srcMap fields are
// ignored.
func buildProviderMap(fset *token.FileSet, hasher typeutil.Hasher, set *ProviderSet) (*typeutil.Map, *typeutil.Map, []error) {
providerMap := new(typeutil.Map)
providerMap.SetHasher(hasher)
srcMap := new(typeutil.Map) // to *providerSetSrc
srcMap.SetHasher(hasher)
ec := new(errorCollector)
// Process injector arguments.
if set.InjectorArgs != nil {
givens := set.InjectorArgs.Tuple
for i := 0; i < givens.Len(); i++ {
typ := givens.At(i).Type()
arg := &InjectorArg{Args: set.InjectorArgs, Index: i}
src := &providerSetSrc{InjectorArg: arg}
if prevSrc := srcMap.At(typ); prevSrc != nil {
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
continue
}
providerMap.Set(typ, &ProvidedType{t: typ, a: arg})
srcMap.Set(typ, src)
}
}
// Process imports, verifying that there are no conflicts between sets.
for _, imp := range set.Imports {
src := &providerSetSrc{Import: imp}
imp.providerMap.Iterate(func(k types.Type, v interface{}) {
if prevSrc := srcMap.At(k); prevSrc != nil {
ec.add(bindingConflictError(fset, k, set, src, prevSrc.(*providerSetSrc)))
return
}
providerMap.Set(k, v)
srcMap.Set(k, src)
})
}
if len(ec.errors) > 0 {
return nil, nil, ec.errors
}
// Process non-binding providers in new set.
for _, p := range set.Providers {
src := &providerSetSrc{Provider: p}
for _, typ := range p.Out {
if prevSrc := srcMap.At(typ); prevSrc != nil {
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
continue
}
providerMap.Set(typ, &ProvidedType{t: typ, p: p})
srcMap.Set(typ, src)
}
}
for _, v := range set.Values {
src := &providerSetSrc{Value: v}
if prevSrc := srcMap.At(v.Out); prevSrc != nil {
ec.add(bindingConflictError(fset, v.Out, set, src, prevSrc.(*providerSetSrc)))
continue
}
providerMap.Set(v.Out, &ProvidedType{t: v.Out, v: v})
srcMap.Set(v.Out, src)
}
for _, f := range set.Fields {
src := &providerSetSrc{Field: f}
for _, typ := range f.Out {
if prevSrc := srcMap.At(typ); prevSrc != nil {
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
continue
}
providerMap.Set(typ, &ProvidedType{t: typ, f: f})
srcMap.Set(typ, src)
}
}
if len(ec.errors) > 0 {
return nil, nil, ec.errors
}
// Process bindings in set. Must happen after the other providers to
// ensure the concrete type is being provided.
for _, b := range set.Bindings {
src := &providerSetSrc{Binding: b}
if prevSrc := srcMap.At(b.Iface); prevSrc != nil {
ec.add(bindingConflictError(fset, b.Iface, set, src, prevSrc.(*providerSetSrc)))
continue
}
concrete := providerMap.At(b.Provided)
if concrete == nil {
setName := set.VarName
if setName == "" {
setName = "provider set"
}
ec.add(notePosition(fset.Position(b.Pos), fmt.Errorf("wire.Bind of concrete type %q to interface %q, but %s does not include a provider for %q", b.Provided, b.Iface, setName, b.Provided)))
continue
}
providerMap.Set(b.Iface, concrete)
srcMap.Set(b.Iface, src)
}
if len(ec.errors) > 0 {
return nil, nil, ec.errors
}
return providerMap, srcMap, nil
}
func verifyAcyclic(providerMap *typeutil.Map, hasher typeutil.Hasher) []error {
// We must visit every provider type inside provider map, but we don't
// have a well-defined starting point and there may be several
// distinct graphs. Thus, we start a depth-first search at every
// provider, but keep a shared record of visited providers to avoid
// duplicating work.
visited := new(typeutil.Map) // to bool
visited.SetHasher(hasher)
ec := new(errorCollector)
// Sort output types so that errors about cycles are consistent.
outputs := providerMap.Keys()
sort.Slice(outputs, func(i, j int) bool { return types.TypeString(outputs[i], nil) < types.TypeString(outputs[j], nil) })
for _, root := range outputs {
// Depth-first search using a stack of trails through the provider map.
stk := [][]types.Type{{root}}
for len(stk) > 0 {
curr := stk[len(stk)-1]
stk = stk[:len(stk)-1]
head := curr[len(curr)-1]
if v, _ := visited.At(head).(bool); v {
continue
}
visited.Set(head, true)
x := providerMap.At(head)
if x == nil {
// Leaf: input.
continue
}
pt := x.(*ProvidedType)
switch {
case pt.IsValue():
// Leaf: values do not have dependencies.
case pt.IsArg():
// Injector arguments do not have dependencies.
case pt.IsProvider() || pt.IsField():
var args []types.Type
if pt.IsProvider() {
for _, arg := range pt.Provider().Args {
args = append(args, arg.Type)
}
} else {
args = append(args, pt.Field().Parent)
}
for _, a := range args {
hasCycle := false
for i, b := range curr {
if types.Identical(a, b) {
sb := new(strings.Builder)
fmt.Fprintf(sb, "cycle for %s:\n", types.TypeString(a, nil))
for j := i; j < len(curr); j++ {
t := providerMap.At(curr[j]).(*ProvidedType)
if t.IsProvider() {
p := t.Provider()
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Pkg.Path(), p.Name)
} else {
p := t.Field()
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Parent, p.Name)
}
}
fmt.Fprintf(sb, "%s", types.TypeString(a, nil))
ec.add(errors.New(sb.String()))
hasCycle = true
break
}
}
if !hasCycle {
next := append(append([]types.Type(nil), curr...), a)
stk = append(stk, next)
}
}
default:
panic("invalid provider map value")
}
}
}
return ec.errors
}
// bindingConflictError creates a new error describing multiple bindings
// for the same output type.
func bindingConflictError(fset *token.FileSet, typ types.Type, set *ProviderSet, cur, prev *providerSetSrc) error {
sb := new(strings.Builder)
if set.VarName != "" {
fmt.Fprintf(sb, "%s has ", set.VarName)
}
fmt.Fprintf(sb, "multiple bindings for %s\n", types.TypeString(typ, nil))
fmt.Fprintf(sb, "current:\n<- %s\n", strings.Join(cur.trace(fset, typ), "\n<- "))
fmt.Fprintf(sb, "previous:\n<- %s", strings.Join(prev.trace(fset, typ), "\n<- "))
return notePosition(fset.Position(set.Pos), errors.New(sb.String()))
}

View File

@ -0,0 +1,493 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package wire
import (
"fmt"
"go/ast"
"golang.org/x/tools/go/ast/astutil"
)
// copyAST performs a deep copy of an AST. *ast.Ident identity will be
// preserved.
//
// This allows using astutil.Apply to rewrite an AST without modifying
// the original AST.
func copyAST(original ast.Node) ast.Node {
// This function is necessarily long. No utility function exists to do this
// clone, as most any attempt would need to have customization options, which
// would need to be as expressive as Apply. A possibility to shorten the code
// here would be to use reflection, but that trades clarity for shorter code.
m := make(map[ast.Node]ast.Node)
astutil.Apply(original, nil, func(c *astutil.Cursor) bool {
switch node := c.Node().(type) {
case nil:
// No-op.
case *ast.ArrayType:
m[node] = &ast.ArrayType{
Lbrack: node.Lbrack,
Len: exprFromMap(m, node.Len),
Elt: exprFromMap(m, node.Elt),
}
case *ast.AssignStmt:
m[node] = &ast.AssignStmt{
Lhs: copyExprList(m, node.Lhs),
TokPos: node.TokPos,
Tok: node.Tok,
Rhs: copyExprList(m, node.Rhs),
}
case *ast.BadDecl:
m[node] = &ast.BadDecl{
From: node.From,
To: node.To,
}
case *ast.BadExpr:
m[node] = &ast.BadExpr{
From: node.From,
To: node.To,
}
case *ast.BadStmt:
m[node] = &ast.BadStmt{
From: node.From,
To: node.To,
}
case *ast.BasicLit:
m[node] = &ast.BasicLit{
ValuePos: node.ValuePos,
Kind: node.Kind,
Value: node.Value,
}
case *ast.BinaryExpr:
m[node] = &ast.BinaryExpr{
X: exprFromMap(m, node.X),
OpPos: node.OpPos,
Op: node.Op,
Y: exprFromMap(m, node.Y),
}
case *ast.BlockStmt:
m[node] = &ast.BlockStmt{
Lbrace: node.Lbrace,
List: copyStmtList(m, node.List),
Rbrace: node.Rbrace,
}
case *ast.BranchStmt:
m[node] = &ast.BranchStmt{
TokPos: node.TokPos,
Tok: node.Tok,
Label: identFromMap(m, node.Label),
}
case *ast.CallExpr:
m[node] = &ast.CallExpr{
Fun: exprFromMap(m, node.Fun),
Lparen: node.Lparen,
Args: copyExprList(m, node.Args),
Ellipsis: node.Ellipsis,
Rparen: node.Rparen,
}
case *ast.CaseClause:
m[node] = &ast.CaseClause{
Case: node.Case,
List: copyExprList(m, node.List),
Colon: node.Colon,
Body: copyStmtList(m, node.Body),
}
case *ast.ChanType:
m[node] = &ast.ChanType{
Begin: node.Begin,
Arrow: node.Arrow,
Dir: node.Dir,
Value: exprFromMap(m, node.Value),
}
case *ast.CommClause:
m[node] = &ast.CommClause{
Case: node.Case,
Comm: stmtFromMap(m, node.Comm),
Colon: node.Colon,
Body: copyStmtList(m, node.Body),
}
case *ast.Comment:
m[node] = &ast.Comment{
Slash: node.Slash,
Text: node.Text,
}
case *ast.CommentGroup:
cg := new(ast.CommentGroup)
if node.List != nil {
cg.List = make([]*ast.Comment, len(node.List))
for i := range node.List {
cg.List[i] = m[node.List[i]].(*ast.Comment)
}
}
m[node] = cg
case *ast.CompositeLit:
m[node] = &ast.CompositeLit{
Type: exprFromMap(m, node.Type),
Lbrace: node.Lbrace,
Elts: copyExprList(m, node.Elts),
Rbrace: node.Rbrace,
}
case *ast.DeclStmt:
m[node] = &ast.DeclStmt{
Decl: m[node.Decl].(ast.Decl),
}
case *ast.DeferStmt:
m[node] = &ast.DeferStmt{
Defer: node.Defer,
Call: callExprFromMap(m, node.Call),
}
case *ast.Ellipsis:
m[node] = &ast.Ellipsis{
Ellipsis: node.Ellipsis,
Elt: exprFromMap(m, node.Elt),
}
case *ast.EmptyStmt:
m[node] = &ast.EmptyStmt{
Semicolon: node.Semicolon,
Implicit: node.Implicit,
}
case *ast.ExprStmt:
m[node] = &ast.ExprStmt{
X: exprFromMap(m, node.X),
}
case *ast.Field:
m[node] = &ast.Field{
Doc: commentGroupFromMap(m, node.Doc),
Names: copyIdentList(m, node.Names),
Type: exprFromMap(m, node.Type),
Tag: basicLitFromMap(m, node.Tag),
Comment: commentGroupFromMap(m, node.Comment),
}
case *ast.FieldList:
fl := &ast.FieldList{
Opening: node.Opening,
Closing: node.Closing,
}
if node.List != nil {
fl.List = make([]*ast.Field, len(node.List))
for i := range node.List {
fl.List[i] = m[node.List[i]].(*ast.Field)
}
}
m[node] = fl
case *ast.ForStmt:
m[node] = &ast.ForStmt{
For: node.For,
Init: stmtFromMap(m, node.Init),
Cond: exprFromMap(m, node.Cond),
Post: stmtFromMap(m, node.Post),
Body: blockStmtFromMap(m, node.Body),
}
case *ast.FuncDecl:
m[node] = &ast.FuncDecl{
Doc: commentGroupFromMap(m, node.Doc),
Recv: fieldListFromMap(m, node.Recv),
Name: identFromMap(m, node.Name),
Type: funcTypeFromMap(m, node.Type),
Body: blockStmtFromMap(m, node.Body),
}
case *ast.FuncLit:
m[node] = &ast.FuncLit{
Type: funcTypeFromMap(m, node.Type),
Body: blockStmtFromMap(m, node.Body),
}
case *ast.FuncType:
m[node] = &ast.FuncType{
Func: node.Func,
Params: fieldListFromMap(m, node.Params),
Results: fieldListFromMap(m, node.Results),
}
case *ast.GenDecl:
decl := &ast.GenDecl{
Doc: commentGroupFromMap(m, node.Doc),
TokPos: node.TokPos,
Tok: node.Tok,
Lparen: node.Lparen,
Rparen: node.Rparen,
}
if node.Specs != nil {
decl.Specs = make([]ast.Spec, len(node.Specs))
for i := range node.Specs {
decl.Specs[i] = m[node.Specs[i]].(ast.Spec)
}
}
m[node] = decl
case *ast.GoStmt:
m[node] = &ast.GoStmt{
Go: node.Go,
Call: callExprFromMap(m, node.Call),
}
case *ast.Ident:
// Keep identifiers the same identity so they can be conveniently
// used with the original *types.Info.
m[node] = node
case *ast.IfStmt:
m[node] = &ast.IfStmt{
If: node.If,
Init: stmtFromMap(m, node.Init),
Cond: exprFromMap(m, node.Cond),
Body: blockStmtFromMap(m, node.Body),
Else: stmtFromMap(m, node.Else),
}
case *ast.ImportSpec:
m[node] = &ast.ImportSpec{
Doc: commentGroupFromMap(m, node.Doc),
Name: identFromMap(m, node.Name),
Path: basicLitFromMap(m, node.Path),
Comment: commentGroupFromMap(m, node.Comment),
EndPos: node.EndPos,
}
case *ast.IncDecStmt:
m[node] = &ast.IncDecStmt{
X: exprFromMap(m, node.X),
TokPos: node.TokPos,
Tok: node.Tok,
}
case *ast.IndexExpr:
m[node] = &ast.IndexExpr{
X: exprFromMap(m, node.X),
Lbrack: node.Lbrack,
Index: exprFromMap(m, node.Index),
Rbrack: node.Rbrack,
}
case *ast.InterfaceType:
m[node] = &ast.InterfaceType{
Interface: node.Interface,
Methods: fieldListFromMap(m, node.Methods),
Incomplete: node.Incomplete,
}
case *ast.KeyValueExpr:
m[node] = &ast.KeyValueExpr{
Key: exprFromMap(m, node.Key),
Colon: node.Colon,
Value: exprFromMap(m, node.Value),
}
case *ast.LabeledStmt:
m[node] = &ast.LabeledStmt{
Label: identFromMap(m, node.Label),
Colon: node.Colon,
Stmt: stmtFromMap(m, node.Stmt),
}
case *ast.MapType:
m[node] = &ast.MapType{
Map: node.Map,
Key: exprFromMap(m, node.Key),
Value: exprFromMap(m, node.Value),
}
case *ast.ParenExpr:
m[node] = &ast.ParenExpr{
Lparen: node.Lparen,
X: exprFromMap(m, node.X),
Rparen: node.Rparen,
}
case *ast.RangeStmt:
m[node] = &ast.RangeStmt{
For: node.For,
Key: exprFromMap(m, node.Key),
Value: exprFromMap(m, node.Value),
TokPos: node.TokPos,
Tok: node.Tok,
X: exprFromMap(m, node.X),
Body: blockStmtFromMap(m, node.Body),
}
case *ast.ReturnStmt:
m[node] = &ast.ReturnStmt{
Return: node.Return,
Results: copyExprList(m, node.Results),
}
case *ast.SelectStmt:
m[node] = &ast.SelectStmt{
Select: node.Select,
Body: blockStmtFromMap(m, node.Body),
}
case *ast.SelectorExpr:
m[node] = &ast.SelectorExpr{
X: exprFromMap(m, node.X),
Sel: identFromMap(m, node.Sel),
}
case *ast.SendStmt:
m[node] = &ast.SendStmt{
Chan: exprFromMap(m, node.Chan),
Arrow: node.Arrow,
Value: exprFromMap(m, node.Value),
}
case *ast.SliceExpr:
m[node] = &ast.SliceExpr{
X: exprFromMap(m, node.X),
Lbrack: node.Lbrack,
Low: exprFromMap(m, node.Low),
High: exprFromMap(m, node.High),
Max: exprFromMap(m, node.Max),
Slice3: node.Slice3,
Rbrack: node.Rbrack,
}
case *ast.StarExpr:
m[node] = &ast.StarExpr{
Star: node.Star,
X: exprFromMap(m, node.X),
}
case *ast.StructType:
m[node] = &ast.StructType{
Struct: node.Struct,
Fields: fieldListFromMap(m, node.Fields),
Incomplete: node.Incomplete,
}
case *ast.SwitchStmt:
m[node] = &ast.SwitchStmt{
Switch: node.Switch,
Init: stmtFromMap(m, node.Init),
Tag: exprFromMap(m, node.Tag),
Body: blockStmtFromMap(m, node.Body),
}
case *ast.TypeAssertExpr:
m[node] = &ast.TypeAssertExpr{
X: exprFromMap(m, node.X),
Lparen: node.Lparen,
Type: exprFromMap(m, node.Type),
Rparen: node.Rparen,
}
case *ast.TypeSpec:
m[node] = &ast.TypeSpec{
Doc: commentGroupFromMap(m, node.Doc),
Name: identFromMap(m, node.Name),
Assign: node.Assign,
Type: exprFromMap(m, node.Type),
Comment: commentGroupFromMap(m, node.Comment),
}
case *ast.TypeSwitchStmt:
m[node] = &ast.TypeSwitchStmt{
Switch: node.Switch,
Init: stmtFromMap(m, node.Init),
Assign: stmtFromMap(m, node.Assign),
Body: blockStmtFromMap(m, node.Body),
}
case *ast.UnaryExpr:
m[node] = &ast.UnaryExpr{
OpPos: node.OpPos,
Op: node.Op,
X: exprFromMap(m, node.X),
}
case *ast.ValueSpec:
m[node] = &ast.ValueSpec{
Doc: commentGroupFromMap(m, node.Doc),
Names: copyIdentList(m, node.Names),
Type: exprFromMap(m, node.Type),
Values: copyExprList(m, node.Values),
Comment: commentGroupFromMap(m, node.Comment),
}
default:
panic(fmt.Sprintf("unhandled AST node: %T", node))
}
return true
})
return m[original]
}
func commentGroupFromMap(m map[ast.Node]ast.Node, key *ast.CommentGroup) *ast.CommentGroup {
if key == nil {
return nil
}
return m[key].(*ast.CommentGroup)
}
func exprFromMap(m map[ast.Node]ast.Node, key ast.Expr) ast.Expr {
if key == nil {
return nil
}
return m[key].(ast.Expr)
}
func stmtFromMap(m map[ast.Node]ast.Node, key ast.Stmt) ast.Stmt {
if key == nil {
return nil
}
return m[key].(ast.Stmt)
}
func identFromMap(m map[ast.Node]ast.Node, key *ast.Ident) *ast.Ident {
if key == nil {
return nil
}
return m[key].(*ast.Ident)
}
func blockStmtFromMap(m map[ast.Node]ast.Node, key *ast.BlockStmt) *ast.BlockStmt {
if key == nil {
return nil
}
return m[key].(*ast.BlockStmt)
}
func fieldListFromMap(m map[ast.Node]ast.Node, key *ast.FieldList) *ast.FieldList {
if key == nil {
return nil
}
return m[key].(*ast.FieldList)
}
func callExprFromMap(m map[ast.Node]ast.Node, key *ast.CallExpr) *ast.CallExpr {
if key == nil {
return nil
}
return m[key].(*ast.CallExpr)
}
func basicLitFromMap(m map[ast.Node]ast.Node, key *ast.BasicLit) *ast.BasicLit {
if key == nil {
return nil
}
return m[key].(*ast.BasicLit)
}
func funcTypeFromMap(m map[ast.Node]ast.Node, key *ast.FuncType) *ast.FuncType {
if key == nil {
return nil
}
return m[key].(*ast.FuncType)
}
func copyExprList(m map[ast.Node]ast.Node, exprs []ast.Expr) []ast.Expr {
if exprs == nil {
return nil
}
newExprs := make([]ast.Expr, len(exprs))
for i := range exprs {
newExprs[i] = m[exprs[i]].(ast.Expr)
}
return newExprs
}
func copyStmtList(m map[ast.Node]ast.Node, stmts []ast.Stmt) []ast.Stmt {
if stmts == nil {
return nil
}
newStmts := make([]ast.Stmt, len(stmts))
for i := range stmts {
newStmts[i] = m[stmts[i]].(ast.Stmt)
}
return newStmts
}
func copyIdentList(m map[ast.Node]ast.Node, idents []*ast.Ident) []*ast.Ident {
if idents == nil {
return nil
}
newIdents := make([]*ast.Ident, len(idents))
for i := range idents {
newIdents[i] = m[idents[i]].(*ast.Ident)
}
return newIdents
}

View File

@ -0,0 +1,84 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package wire
import (
"go/token"
)
// errorCollector manages a list of errors. The zero value is an empty list.
type errorCollector struct {
errors []error
}
// add appends any non-nil errors to the collector.
func (ec *errorCollector) add(errs ...error) {
for _, e := range errs {
if e != nil {
ec.errors = append(ec.errors, e)
}
}
}
// mapErrors returns a new slice that wraps any errors using the given function.
func mapErrors(errs []error, f func(error) error) []error {
if len(errs) == 0 {
return nil
}
newErrs := make([]error, len(errs))
for i := range errs {
newErrs[i] = f(errs[i])
}
return newErrs
}
// A wireErr is an error with an optional position.
type wireErr struct {
error error
position token.Position
}
// notePosition wraps an error with position information if it doesn't already
// have it.
//
// notePosition is usually called multiple times as an error goes up the call
// stack, so calling notePosition on an existing *wireErr will not modify the
// position, as the assumption is that deeper calls have more precise position
// information about the source of the error.
func notePosition(p token.Position, e error) error {
switch e.(type) {
case nil:
return nil
case *wireErr:
return e
default:
return &wireErr{error: e, position: p}
}
}
// notePositionAll wraps a list of errors with the given position.
func notePositionAll(p token.Position, errs []error) []error {
return mapErrors(errs, func(e error) error {
return notePosition(p, e)
})
}
// Error returns the error message prefixed by the position if valid.
func (w *wireErr) Error() string {
if !w.position.IsValid() {
return w.error.Error()
}
return w.position.String() + ": " + w.error.Error()
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
)
func main() {
fmt.Println(inject(Foo{"hello"}).Name)
}
type Fooer interface {
Foo() string
}
type Foo struct {
f string
}
func (f Foo) Foo() string {
return f.f
}
type Bar struct {
Name string
}
func NewBar(fooer Fooer) *Bar {
return &Bar{Name: fooer.Foo()}
}

View File

@ -0,0 +1,29 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
func inject(foo Foo) *Bar {
wire.Build(
NewBar,
wire.Bind(new(Fooer), new(Foo)),
)
return nil
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
hello

View File

@ -0,0 +1,14 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
func inject(foo Foo) *Bar {
bar := NewBar(foo)
return bar
}

View File

@ -0,0 +1,43 @@
// Copyright 2019 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
)
func main() {
fmt.Println(inject(&Foo{"hello"}).Name)
}
type Fooer interface {
Foo() string
}
type Foo struct {
f string
}
func (f *Foo) Foo() string {
return f.f
}
type Bar struct {
Name string
}
func NewBar(fooer Fooer) *Bar {
return &Bar{Name: fooer.Foo()}
}

View File

@ -0,0 +1,29 @@
// Copyright 2019 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
func inject(foo *Foo) *Bar {
wire.Build(
NewBar,
wire.Bind(new(Fooer), new(*Foo)),
)
return nil
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
hello

View File

@ -0,0 +1,14 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
func inject(foo *Foo) *Bar {
bar := NewBar(foo)
return bar
}

View File

@ -0,0 +1,20 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
func main() {
w := inject()
w.Write([]byte("Hello, World!"))
}

View File

@ -0,0 +1,32 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//+build wireinject
package main
import (
"io"
"os"
"github.com/grafana/grafana/pkg/build/wire"
)
func inject() io.Writer {
wire.Build(
wire.Value(os.Stdout),
wire.Bind(new(io.Writer), new(*os.File)),
)
return nil
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
Hello, World!

View File

@ -0,0 +1,23 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
import (
"io"
"os"
)
// Injectors from wire.go:
func inject() io.Writer {
file := _wireFileValue
return file
}
var (
_wireFileValue = os.Stdout
)

View File

@ -0,0 +1,23 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build !wireinject
// Package bar includes both wireinject and non-wireinject variants.
package bar
import "github.com/grafana/grafana/pkg/build/wire"
// Set provides an unfriendly user greeting.
var Set = wire.NewSet(wire.Value("Bah humbug! This is the wrong variant!"))

View File

@ -0,0 +1,22 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package bar
import "github.com/grafana/grafana/pkg/build/wire"
// Set provides a friendly user greeting.
var Set = wire.NewSet(wire.Value("Hello, World!"))

View File

@ -0,0 +1,21 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "fmt"
func main() {
fmt.Println(injectedMessage())
}

View File

@ -0,0 +1,27 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"example.com/bar"
"github.com/grafana/grafana/pkg/build/wire"
)
func injectedMessage() string {
wire.Build(bar.Set)
return ""
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
Hello, World!

View File

@ -0,0 +1,18 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
string2 := _wireStringValue
return string2
}
var (
_wireStringValue = "Hello, World!"
)

View File

@ -0,0 +1,40 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"github.com/grafana/grafana/pkg/build/wire"
)
func main() {
fmt.Println(injectFooBar())
}
type Foo int
type FooBar int
var Set = wire.NewSet(
provideFoo,
provideFooBar)
func provideFoo() Foo {
return 41
}
func provideFooBar(foo Foo) FooBar {
return FooBar(foo) + 1
}

View File

@ -0,0 +1,26 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
func injectFooBar() FooBar {
wire.Build(Set)
return 0
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
42

View File

@ -0,0 +1,15 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
func injectFooBar() FooBar {
foo := provideFoo()
fooBar := provideFooBar(foo)
return fooBar
}

View File

@ -0,0 +1,46 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
)
func main() {
bar, cleanup := injectBar()
fmt.Println(*bar)
cleanup()
fmt.Println(*bar)
}
type Foo int
type Bar int
func provideFoo() (*Foo, func()) {
foo := new(Foo)
*foo = 42
return foo, func() { *foo = 0 }
}
func provideBar(foo *Foo) (*Bar, func()) {
bar := new(Bar)
*bar = 77
return bar, func() {
if *foo == 0 {
panic("foo cleaned up before bar")
}
*bar = 0
}
}

View File

@ -0,0 +1,26 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
func injectBar() (*Bar, func()) {
wire.Build(provideFoo, provideBar)
return nil, nil
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1,2 @@
77
0

View File

@ -0,0 +1,18 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
func injectBar() (*Bar, func()) {
foo, cleanup := provideFoo()
bar, cleanup2 := provideBar(foo)
return bar, func() {
cleanup2()
cleanup()
}
}

View File

@ -0,0 +1,40 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
// All of the declarations are in one file.
// Wire should copy non-injectors over, preserving imports.
package main
import (
"fmt"
"github.com/grafana/grafana/pkg/build/wire"
)
func main() {
fmt.Println(injectedMessage())
}
// provideMessage provides a friendly user greeting.
func provideMessage() string {
return "Hello, World!"
}
func injectedMessage() string {
wire.Build(provideMessage)
return ""
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
Hello, World!

View File

@ -0,0 +1,29 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
import (
"fmt"
)
// Injectors from foo.go:
func injectedMessage() string {
string2 := provideMessage()
return string2
}
// foo.go:
func main() {
fmt.Println(injectedMessage())
}
// provideMessage provides a friendly user greeting.
func provideMessage() string {
return "Hello, World!"
}

View File

@ -0,0 +1,37 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "fmt"
func main() {
fmt.Println(injectedBaz())
}
type Foo int
type Bar int
type Baz int
func provideFoo(_ Baz) Foo {
return 0
}
func provideBar(_ Foo) Bar {
return 0
}
func provideBaz(_ Bar) Baz {
return 0
}

View File

@ -0,0 +1,26 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
func injectedBaz() Baz {
wire.Build(provideFoo, provideBar, provideBaz)
return 0
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1,5 @@
example.com/foo/wire.go:x:y: cycle for example.com/foo.Bar:
example.com/foo.Bar (example.com/foo.provideBar) ->
example.com/foo.Foo (example.com/foo.provideFoo) ->
example.com/foo.Baz (example.com/foo.provideBaz) ->
example.com/foo.Bar

View File

@ -0,0 +1,28 @@
// Copyright 2020 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
)
type (
Bar struct{}
Foo struct{}
)
func main() {
fmt.Println("Hello, World")
}

View File

@ -0,0 +1,31 @@
// Copyright 2020 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
/* blockComment returns Foo and has a /*- style doc comment */
func blockComment() *Foo {
panic(wire.Build(wire.Struct(new(Foo))))
}
// lineComment returns Bar and has a //- style doc comment
func lineComment() *Bar {
panic(wire.Build(wire.Struct(new(Bar))))
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
Hello, World

View File

@ -0,0 +1,21 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
/* blockComment returns Foo and has a /*- style doc comment */
func blockComment() *Foo {
foo := &Foo{}
return foo
}
// lineComment returns Bar and has a //- style doc comment
func lineComment() *Bar {
bar := &Bar{}
return bar
}

View File

@ -0,0 +1,25 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
)
func main() {
fmt.Println(injectedMessage())
}
var myFakeSet struct{}

View File

@ -0,0 +1,26 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
func injectedMessage() string {
wire.Build(myFakeSet)
return ""
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
example.com/foo/wire.go:x:y: var example.com/foo.myFakeSet struct{} is not a provider or a provider set

View File

@ -0,0 +1,120 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This test demonstrates how to use mocks with wire.
package main
import (
"fmt"
"time"
"github.com/grafana/grafana/pkg/build/wire"
)
func main() {
// Create a "real" greeter.
// Greet() will include the real current time, so elide it for repeatable
// tests.
fmt.Printf("Real time greeting: %s [current time elided]\n", initApp().Greet()[0:15])
// There are two approaches for creating an app with mocks.
// Approach A: create the mocks manually, and pass them to an injector.
// This approach is useful if you need to prime the mocks beforehand.
fmt.Println("Approach A")
mt := newMockTimer()
mockedApp := initMockedAppFromArgs(mt)
fmt.Println(mockedApp.Greet()) // prints greeting with time = zero time
mt.T = mt.T.AddDate(1999, 0, 0)
fmt.Println(mockedApp.Greet()) // prints greeting with time = year 2000
// Approach B: allow the injector to create the mocks, and return a struct
// that includes the resulting app plus the mocks.
fmt.Println("Approach B")
appWithMocks := initMockedApp()
fmt.Println(appWithMocks.app.Greet()) // prints greeting with time = zero time
appWithMocks.mt.T = appWithMocks.mt.T.AddDate(999, 0, 0)
fmt.Println(appWithMocks.app.Greet()) // prints greeting with time = year 1000
}
// appSet is a provider set for creating a real app.
var appSet = wire.NewSet(
wire.Struct(new(app), "*"),
wire.Struct(new(greeter), "*"),
wire.InterfaceValue(new(timer), realTime{}),
)
// appSetWithoutMocks is a provider set for creating an app with mocked
// dependencies. The mocked dependencies are omitted and must be provided as
// arguments to the injector.
// It is used for Approach A.
var appSetWithoutMocks = wire.NewSet(
wire.Struct(new(app), "*"),
wire.Struct(new(greeter), "*"),
)
// mockAppSet is a provider set for creating a mocked app, including the mocked
// dependencies.
// It is used for Approach B.
var mockAppSet = wire.NewSet(
wire.Struct(new(app), "*"),
wire.Struct(new(greeter), "*"),
wire.Struct(new(appWithMocks), "*"),
// For each mocked dependency, add a provider and use wire.Bind to bind
// the concrete type to the relevant interface.
newMockTimer,
wire.Bind(new(timer), new(*mockTimer)),
)
type timer interface {
Now() time.Time
}
// realTime implements timer with the real time.
type realTime struct{}
func (realTime) Now() time.Time { return time.Now() }
// mockTimer implements timer using a mocked time.
type mockTimer struct {
T time.Time
}
func newMockTimer() *mockTimer { return &mockTimer{} }
func (m *mockTimer) Now() time.Time { return m.T }
// greeter issues greetings with the time provided by T.
type greeter struct {
T timer
}
func (g greeter) Greet() string {
return fmt.Sprintf("Good day! It is %v", g.T.Now())
}
type app struct {
g greeter
}
func (a app) Greet() string {
return a.g.Greet()
}
// appWithMocks is used for Approach B, to return the app plus its mocked
// dependencies.
type appWithMocks struct {
app app
mt *mockTimer
}

View File

@ -0,0 +1,42 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
// initApp returns a real app.
func initApp() *app {
wire.Build(appSet)
return nil
}
// initMockedAppFromArgs returns an app with mocked dependencies provided via
// arguments (Approach A). Note that the argument's type is the interface
// type (timer), but the concrete mock type should be passed.
func initMockedAppFromArgs(mt timer) *app {
wire.Build(appSetWithoutMocks)
return nil
}
// initMockedApp returns an app with its mocked dependencies, created
// via providers (Approach B).
func initMockedApp() *appWithMocks {
wire.Build(mockAppSet)
return nil
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1,7 @@
Real time greeting: Good day! It is [current time elided]
Approach A
Good day! It is 0001-01-01 00:00:00 +0000 UTC
Good day! It is 2000-01-01 00:00:00 +0000 UTC
Approach B
Good day! It is 0001-01-01 00:00:00 +0000 UTC
Good day! It is 1000-01-01 00:00:00 +0000 UTC

View File

@ -0,0 +1,55 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
// initApp returns a real app.
func initApp() *app {
mainTimer := _wireRealTimeValue
mainGreeter := greeter{
T: mainTimer,
}
mainApp := &app{
g: mainGreeter,
}
return mainApp
}
var (
_wireRealTimeValue = realTime{}
)
// initMockedAppFromArgs returns an app with mocked dependencies provided via
// arguments (Approach A). Note that the argument's type is the interface
// type (timer), but the concrete mock type should be passed.
func initMockedAppFromArgs(mt timer) *app {
mainGreeter := greeter{
T: mt,
}
mainApp := &app{
g: mainGreeter,
}
return mainApp
}
// initMockedApp returns an app with its mocked dependencies, created
// via providers (Approach B).
func initMockedApp() *appWithMocks {
mainMockTimer := newMockTimer()
mainGreeter := greeter{
T: mainMockTimer,
}
mainApp := app{
g: mainGreeter,
}
mainAppWithMocks := &appWithMocks{
app: mainApp,
mt: mainMockTimer,
}
return mainAppWithMocks
}

View File

@ -0,0 +1,21 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bar
import "github.com/grafana/grafana/pkg/build/wire"
var Value = wire.Value(PublicMsg)
var PublicMsg = "Hello, World!"

View File

@ -0,0 +1,21 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "fmt"
func main() {
fmt.Println(injectedMessage())
}

View File

@ -0,0 +1,27 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"example.com/bar"
"github.com/grafana/grafana/pkg/build/wire"
)
func injectedMessage() string {
wire.Build(bar.Value)
return ""
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
Hello, World!

View File

@ -0,0 +1,22 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
import (
"example.com/bar"
)
// Injectors from wire.go:
func injectedMessage() string {
string2 := _wireStringValue
return string2
}
var (
_wireStringValue = bar.PublicMsg
)

View File

@ -0,0 +1,23 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bar
import (
"os"
"github.com/grafana/grafana/pkg/build/wire"
)
var Value = wire.Value(os.Stdout)

View File

@ -0,0 +1,21 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "fmt"
func main() {
fmt.Fprintln(injectedFile(), "Hello, World!")
}

View File

@ -0,0 +1,29 @@
// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"os"
"example.com/bar"
"github.com/grafana/grafana/pkg/build/wire"
)
func injectedFile() *os.File {
wire.Build(bar.Value)
return nil
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1 @@
Hello, World!

View File

@ -0,0 +1,22 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
import (
"os"
)
// Injectors from wire.go:
func injectedFile() *os.File {
file := _wireFileValue
return file
}
var (
_wireFileValue = os.Stdout
)

View File

@ -0,0 +1,38 @@
// Copyright 2019 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
)
func main() {
fmt.Println(injectedBaz())
}
type Foo int
type Baz int
type Bar struct {
Bz Baz
}
func provideFoo(_ Baz) Foo {
return 0
}
func provideBar(_ Foo) Bar {
return Bar{}
}

View File

@ -0,0 +1,26 @@
// Copyright 2019 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"github.com/grafana/grafana/pkg/build/wire"
)
func injectedBaz() Baz {
wire.Build(provideFoo, provideBar, wire.FieldsOf(new(Bar), "Bz"))
return 0
}

View File

@ -0,0 +1 @@
example.com/foo

View File

@ -0,0 +1,5 @@
example.com/foo/wire.go:x:y: cycle for example.com/foo.Bar:
example.com/foo.Bar (example.com/foo.provideBar) ->
example.com/foo.Foo (example.com/foo.provideFoo) ->
example.com/foo.Baz (example.com/foo.Bar.Bz) ->
example.com/foo.Bar

Some files were not shown because too many files have changed in this diff Show More