├── .gitignore
├── .travis.yml
├── Gopkg.lock
├── Gopkg.toml
├── LICENSE
├── Makefile
├── README.md
├── client.go
├── client_test.go
├── doc.go
├── errors.go
├── errors_test.go
├── types.go
├── types_test.go
└── vendor
└── github.com
├── google
└── go-cmp
│ ├── .travis.yml
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── cmp
│ ├── cmpopts
│ │ ├── equate.go
│ │ ├── ignore.go
│ │ ├── sort.go
│ │ ├── struct_filter.go
│ │ ├── util_test.go
│ │ └── xform.go
│ ├── compare.go
│ ├── compare_test.go
│ ├── example_reporter_test.go
│ ├── example_test.go
│ ├── export_panic.go
│ ├── export_unsafe.go
│ ├── internal
│ │ ├── diff
│ │ │ ├── debug_disable.go
│ │ │ ├── debug_enable.go
│ │ │ ├── diff.go
│ │ │ └── diff_test.go
│ │ ├── flags
│ │ │ ├── flags.go
│ │ │ ├── toolchain_legacy.go
│ │ │ └── toolchain_recent.go
│ │ ├── function
│ │ │ ├── func.go
│ │ │ └── func_test.go
│ │ ├── testprotos
│ │ │ └── protos.go
│ │ ├── teststructs
│ │ │ ├── project1.go
│ │ │ ├── project2.go
│ │ │ ├── project3.go
│ │ │ ├── project4.go
│ │ │ └── structs.go
│ │ └── value
│ │ │ ├── pointer_purego.go
│ │ │ ├── pointer_unsafe.go
│ │ │ ├── sort.go
│ │ │ ├── sort_test.go
│ │ │ ├── zero.go
│ │ │ └── zero_test.go
│ ├── options.go
│ ├── options_test.go
│ ├── path.go
│ ├── report.go
│ ├── report_compare.go
│ ├── report_reflect.go
│ ├── report_slices.go
│ ├── report_text.go
│ └── report_value.go
│ └── go.mod
└── pkg
└── errors
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── appveyor.yml
├── bench_test.go
├── errors.go
├── errors_test.go
├── example_test.go
├── format_test.go
├── stack.go
└── stack_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.dll
4 | *.so
5 | *.dylib
6 |
7 | # Test binary, build with `go test -c`
8 | *.test
9 |
10 | # Output of the go coverage tool, specifically when used with LiteIDE
11 | *.out
12 |
13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
14 | .glide/
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2017, 2019 Tim Heckman
2 | # Use of this source code is governed by the MIT License that can be found in
3 | # the LICENSE file at the root of this repository.
4 |
5 | language: go
6 | go:
7 | - 1.12.x
8 | - 1.13.x
9 | env:
10 | - GO111MODULE=off
11 | sudo: false
12 | notifications:
13 | email:
14 | on_success: never
15 | on_failure: always
16 | before_install: make prebuild
17 | install: dep ensure
18 |
--------------------------------------------------------------------------------
/Gopkg.lock:
--------------------------------------------------------------------------------
1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
2 |
3 |
4 | [[projects]]
5 | name = "github.com/google/go-cmp"
6 | packages = [
7 | "cmp",
8 | "cmp/internal/diff",
9 | "cmp/internal/flags",
10 | "cmp/internal/function",
11 | "cmp/internal/value"
12 | ]
13 | revision = "2d0692c2e9617365a95b295612ac0d4415ba4627"
14 | version = "v0.3.1"
15 |
16 | [[projects]]
17 | name = "github.com/pkg/errors"
18 | packages = ["."]
19 | revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
20 | version = "v0.8.0"
21 |
22 | [solve-meta]
23 | analyzer-name = "dep"
24 | analyzer-version = 1
25 | inputs-digest = "0af4645a5e35ef4f963e9427c4cb010cbdafb554b5438bf64e0e77e4276cade5"
26 | solver-name = "gps-cdcl"
27 | solver-version = 1
28 |
--------------------------------------------------------------------------------
/Gopkg.toml:
--------------------------------------------------------------------------------
1 | # Gopkg.toml example
2 | #
3 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
4 | # for detailed Gopkg.toml documentation.
5 | #
6 | # required = ["github.com/user/thing/cmd/thing"]
7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
8 | #
9 | # [[constraint]]
10 | # name = "github.com/user/project"
11 | # version = "1.0.0"
12 | #
13 | # [[constraint]]
14 | # name = "github.com/user/project2"
15 | # branch = "dev"
16 | # source = "github.com/myfork/project2"
17 | #
18 | # [[override]]
19 | # name = "github.com/x/y"
20 | # version = "2.4.0"
21 |
22 |
23 | [[constraint]]
24 | name = "github.com/pkg/errors"
25 | version = "0.8.0"
26 |
27 | [[constraint]]
28 | name = "github.com/google/go-cmp"
29 | version = "0.3.1"
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017, 2019 Tim Heckman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2017, 2019 Tim Heckman
2 | # Use of this source code is governed by the MIT License that can be found in
3 | # the LICENSE file at the root of this repository.
4 |
5 | test: vet lint staticcheck tests
6 |
7 | prebuild:
8 | go get -u github.com/golang/dep/cmd/dep \
9 | golang.org/x/lint/golint \
10 | honnef.co/go/tools/cmd/staticcheck \
11 | golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
12 |
13 | # this needs to be updated when a future version of 1.13.x includes:
14 | # https://github.com/golang/go/issues/34053
15 | #
16 | # original line now removed:
17 | # go vet -vettool=$(shell which shadow) ./...
18 | vet:
19 | go vet ./...
20 | shadow ./...
21 |
22 | lint:
23 | golint -set_exit_status
24 |
25 | staticcheck:
26 | staticcheck ./...
27 |
28 | tests:
29 | go test -race -covermode atomic -cover -coverprofile profile.out ./...
30 | go tool cover -func=profile.out
31 |
32 | .PHONY: test prebuild vet lint staticcheck tests
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ipdata
2 | [](https://github.com/theckman/go-ipdata/blob/master/LICENSE)
3 | [](https://godoc.org/github.com/theckman/go-ipdata)
4 | [](https://github.com/theckman/go-ipdata/releases)
5 | [](https://travis-ci.org/theckman/go-ipdata/branches)
6 | [](https://gocover.io/github.com/theckman/go-ipdata)
7 | [](https://goreportcard.com/report/github.com/theckman/go-ipdata)
8 |
9 | Package ipdata is a client for the https://ipdata.co API. It provides functions
10 | for looking up data, as well as parsing the data in a programmatic way. The
11 | simplest usage is to build a new client and then use the `Lookup` method.
12 |
13 | ## License
14 | This code is released under the MIT License. Please see the
15 | [LICENSE](https://github.com/theckman/go-ipdata/blob/master/LICENSE) for the
16 | full content of the license.
17 |
18 | ## Contributing
19 | If you'd like to contribute to this project, I welcome any pull requests against
20 | this repo. The only ask is that a GitHub issue be opened detailing the desired
21 | functionality before making any pull requests.
22 |
23 | ## Usage
24 | The service provided by `ipdata` requires an API key before making API calls.
25 | Attempts to create a client without one will fail, as would attempts to contact
26 | the API. You can get an API key from https://ipdata.co/.
27 |
28 | Here is a simple example of using the library:
29 |
30 | ```Go
31 | import (
32 | "github.com/ipdata/go"
33 | "fmt"
34 | )
35 |
36 | ipd, _ := ipdata.NewClient("EXAMPLE_API_KEY")
37 |
38 | data, err := ipd.Lookup("8.8.8.8")
39 | if err != nil {
40 | // handle error
41 | }
42 |
43 | fmt.Printf("%s (%s)\n", data.IP, data.ASN)
44 | ```
45 |
46 | Errors returned from the lookup function calls may be of type `Error`, which
47 | includes the message from the API and the HTTP status code. The `Error()` method
48 | on this type only returns the message and not the status code. To maintain
49 | compatibility with Go 1.12.x, this is still using github.com/pkg/errors for
50 | error management:
51 |
52 | ```Go
53 | import "github.com/pkg/errors"
54 |
55 | data, err := ipd.Lookup("8.8.8.8")
56 | if err != nil {
57 | // do a type assertion on the error
58 | rerr, ok := errors.Cause(err).(ipdata.Error)
59 |
60 | if !ok {
61 | // this wasn't a failure from rate limiting
62 | }
63 |
64 | fmt.Println("%d: %s", rerr.Code(), rerr.Error())
65 | }
66 | ```
67 |
68 | ## Contributors
69 |
70 | - [Tim Heckman](https://github.com/theckman/) - Created the first version of this library
71 |
--------------------------------------------------------------------------------
/client.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017, 2019 Tim Heckman
2 | // Use of this source code is governed by the MIT License that can be found in
3 | // the LICENSE file at the root of this repository.
4 |
5 | package ipdata
6 |
7 | import (
8 | "bytes"
9 | "context"
10 | "encoding/json"
11 | "fmt"
12 | "io"
13 | "io/ioutil"
14 | "net"
15 | "net/http"
16 | "net/url"
17 | "runtime"
18 | "time"
19 |
20 | "github.com/pkg/errors"
21 | )
22 |
23 | // Version is the package version
24 | const Version = "0.7.1"
25 |
26 | // fqpn is the Fully Qualified Package Name for use in the client's User-Agent
27 | const fqpn = "github.com/theckman/go-ipdata"
28 |
29 | const (
30 | apiEndpoint = "https://api.ipdata.co/"
31 | apiAuthParam = "api-key"
32 | )
33 |
34 | var userAgent = fmt.Sprintf(
35 | "go-ipdata/%s (%s) Go-http-client/%s (%s %s)",
36 | Version, fqpn, runtime.Version(), runtime.GOOS, runtime.GOARCH,
37 | )
38 |
39 | var errAPIKey = errors.New("apiKey cannot be an empty string")
40 |
41 | // Client is the struct to represent the functionality presented by the
42 | // https://ipdata.co API.
43 | type Client struct {
44 | c *http.Client // http client
45 | e string // api endpoint
46 | k string // api key
47 | }
48 |
49 | // NewClient takes an optional API key and returns a Client. If you do not have
50 | // an API key use an empty string ("").
51 | func NewClient(apiKey string) (Client, error) {
52 | if len(apiKey) == 0 {
53 | return Client{}, errAPIKey
54 | }
55 |
56 | return Client{
57 | c: newHTTPClient(),
58 | e: apiEndpoint,
59 | k: apiKey,
60 | }, nil
61 | }
62 |
63 | type apiErr struct {
64 | Message string `json:"message"`
65 | }
66 |
67 | // RawLookup uses the internal mechanics to make an HTTP request to the API and
68 | // returns the HTTP response. This allows consumers of the API to implement
69 | // their own behaviors. If an API error occurs, the error value will be of type
70 | // Error.
71 | func (c Client) RawLookup(ip string) (*http.Response, error) {
72 | return c.RawLookupWithContext(context.Background(), ip)
73 | }
74 |
75 | // RawLookupWithContext is a RawLookup that uses a provided context.Context.
76 | func (c Client) RawLookupWithContext(ctx context.Context, ip string) (*http.Response, error) {
77 | // build request
78 | req, err := newGetRequestWithContext(ctx, c.e+ip, c.k)
79 | if err != nil {
80 | return nil, errors.Wrapf(err, "error building request to look up %s", ip)
81 | }
82 |
83 | // action request
84 | resp, err := c.c.Do(req)
85 | if err != nil {
86 | return nil, errors.Wrapf(err, "http request to %q failed", req.URL.Scheme+"://"+req.URL.Host+req.URL.Path)
87 | }
88 |
89 | switch resp.StatusCode {
90 | case http.StatusOK: // 200
91 | // we can try and parse
92 | return resp, nil
93 | default:
94 | // provide response body as error to consumer
95 | body, err := ioutil.ReadAll(resp.Body)
96 | if err != nil {
97 | return nil, errors.Wrapf(err, "failed to read body from response with status code %q: %s", resp.Status, err)
98 | }
99 |
100 | var a apiErr
101 |
102 | if err := json.Unmarshal(body, &a); err != nil {
103 | return nil, errors.Errorf("request for %q failed (unexpected response): %s: %v", ip, resp.Status, err)
104 | }
105 |
106 | return nil, newError(a.Message, resp.StatusCode)
107 | }
108 | }
109 |
110 | func decodeIP(r io.Reader) (IP, error) {
111 | body, err := ioutil.ReadAll(r)
112 | if err != nil {
113 | return IP{}, err
114 | }
115 |
116 | ip := IP{}
117 |
118 | if err := json.Unmarshal(body, &ip); err != nil {
119 | return IP{}, fmt.Errorf("failed to parse JSON: %s", err)
120 | }
121 |
122 | return ip, nil
123 | }
124 |
125 | // Lookup takes an IP address to look up the details for. An empty string means
126 | // you want the information about the current node's pubilc IP address. If an
127 | // API error occurs, the error value will be of type Error.
128 | func (c Client) Lookup(ip string) (IP, error) {
129 | return c.LookupWithContext(context.Background(), ip)
130 | }
131 |
132 | // LookupWithContext is a Lookup that uses a provided context.Context.
133 | func (c Client) LookupWithContext(ctx context.Context, ip string) (IP, error) {
134 | resp, err := c.RawLookupWithContext(ctx, ip)
135 | if err != nil {
136 | return IP{}, err
137 | }
138 |
139 | defer func() {
140 | _, _ = io.Copy(ioutil.Discard, resp.Body)
141 | _ = resp.Body.Close()
142 | }()
143 |
144 | pip, err := decodeIP(resp.Body)
145 | if err != nil {
146 | return IP{}, err
147 | }
148 |
149 | return pip, nil
150 | }
151 |
152 | func newGetRequestWithContext(ctx context.Context, urlStr, apiKey string) (*http.Request, error) {
153 | if len(urlStr) == 0 {
154 | return nil, errors.New("url cannot be an empty string")
155 | }
156 |
157 | if len(apiKey) == 0 {
158 | return nil, errAPIKey
159 | }
160 |
161 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlStr, nil)
162 | if err != nil {
163 | return nil, err
164 | }
165 |
166 | req.Header.Set("User-Agent", userAgent)
167 | req.Header.Set("Accept", "application/json")
168 |
169 | q := url.Values{apiAuthParam: []string{apiKey}}
170 | req.URL.RawQuery = q.Encode()
171 |
172 | return req, nil
173 | }
174 |
175 | // RawBulkLookup takes a set of IP addresses, and returns the response from the
176 | // API.
177 | func (c *Client) RawBulkLookup(ips []string) (*http.Response, error) {
178 | return c.RawBulkLookupWithContext(context.Background(), ips)
179 | }
180 |
181 | // RawBulkLookupWithContext is a RawBulkLookup with a provided context.Context.
182 | func (c *Client) RawBulkLookupWithContext(ctx context.Context, ips []string) (*http.Response, error) {
183 | // build request
184 | req, err := newBulkPostRequestWithContext(ctx, c.e+"bulk", c.k, ips)
185 | if err != nil {
186 | return nil, errors.Wrap(err, "error building bulk lookup request")
187 | }
188 |
189 | // action request
190 | resp, err := c.c.Do(req)
191 | if err != nil {
192 | return nil, errors.Wrapf(err, "http request to %q failed", req.URL.Scheme+"://"+req.URL.Host+req.URL.Path)
193 | }
194 |
195 | switch resp.StatusCode {
196 | case http.StatusOK: // 200
197 | // we can try and parse
198 | return resp, nil
199 | default:
200 | // provide response body as error to consumer
201 | body, err := ioutil.ReadAll(resp.Body)
202 | if err != nil {
203 | return nil, errors.Wrapf(err, "failed to read body from response with status code %q: %s", resp.Status, err)
204 | }
205 |
206 | var a apiErr
207 |
208 | if err := json.Unmarshal(body, &a); err != nil {
209 | return nil, errors.Errorf("request failed (unexpected response): %s: %v", resp.Status, err)
210 | }
211 |
212 | return nil, newError(a.Message, resp.StatusCode)
213 | }
214 | }
215 |
216 | func newBulkPostRequestWithContext(ctx context.Context, urlStr, apiKey string, ips []string) (*http.Request, error) {
217 | if len(urlStr) == 0 {
218 | return nil, errors.New("url cannot be an empty string")
219 | }
220 |
221 | if len(apiKey) == 0 {
222 | return nil, errAPIKey
223 | }
224 |
225 | if len(ips) == 0 {
226 | return nil, errors.New("must provide at least one IP")
227 | }
228 |
229 | if err := ctx.Err(); err != nil {
230 | return nil, err
231 | }
232 |
233 | buf := &bytes.Buffer{}
234 | if err := json.NewEncoder(buf).Encode(ips); err != nil {
235 | return nil, errors.Wrap(err, "failed to encode JSON")
236 | }
237 |
238 | req, err := http.NewRequestWithContext(ctx, http.MethodPost, urlStr, buf)
239 | if err != nil {
240 | return nil, err
241 | }
242 |
243 | req.Header.Set("User-Agent", userAgent)
244 | req.Header.Set("Accept", "application/json")
245 | req.Header.Set("Content-Type", "application/json")
246 |
247 | q := url.Values{apiAuthParam: []string{apiKey}}
248 | req.URL.RawQuery = q.Encode()
249 |
250 | return req, nil
251 | }
252 |
253 | // BulkLookup takes a set of IP addresses, and returns a set of results from the
254 | // API. If the request failed, or something was wrong with one of the inputs,
255 | // the error value will be of type Error. If err is non-nil, the []*IP slice may
256 | // contain data (if it was able to process some of the inputs). The error value
257 | // will contain the index of the first error in the bulk response.
258 | //
259 | // Please note, any IPs that had a failed lookup will be a nil entry in the
260 | // slice when an error is returned. So if you start to use the []*IP when err !=
261 | // nil, you will need to add explicit nil checks to avoid pointer derefence
262 | // panics.
263 | func (c *Client) BulkLookup(ips []string) ([]*IP, error) {
264 | return c.BulkLookupWithContext(context.Background(), ips)
265 | }
266 |
267 | // BulkLookupWithContext is a BulkLookup with a provided context.Context.
268 | func (c *Client) BulkLookupWithContext(ctx context.Context, ips []string) ([]*IP, error) {
269 | resp, err := c.RawBulkLookupWithContext(ctx, ips)
270 | if err != nil {
271 | return nil, err
272 | }
273 |
274 | defer func() {
275 | _, _ = io.Copy(ioutil.Discard, resp.Body)
276 | _ = resp.Body.Close()
277 | }()
278 |
279 | body, err := ioutil.ReadAll(resp.Body)
280 | if err != nil {
281 | return nil, errors.Wrap(err, "failed to read response body")
282 | }
283 |
284 | var bip []bulkIP
285 |
286 | if err := json.Unmarshal(body, &bip); err != nil {
287 | return nil, errors.Wrap(err, "failed to parse JSON")
288 | }
289 |
290 | res := make([]*IP, len(bip))
291 | var retErr error
292 |
293 | for i, ip := range bip {
294 | if len(ip.Message) > 0 && retErr == nil {
295 | retErr = Error{
296 | m: ip.Message,
297 | c: resp.StatusCode,
298 | i: i,
299 | }
300 | continue
301 | }
302 |
303 | res[i] = bulkToIP(ip)
304 | }
305 |
306 | if retErr != nil {
307 | return res, retErr
308 | }
309 |
310 | return res, nil // avoid nil interface check problem
311 | }
312 |
313 | func newHTTPClient() *http.Client {
314 | return &http.Client{
315 | Transport: &http.Transport{
316 | Proxy: http.ProxyFromEnvironment,
317 | DialContext: (&net.Dialer{
318 | Timeout: 30 * time.Second,
319 | KeepAlive: 30 * time.Second,
320 | DualStack: true,
321 | }).DialContext,
322 | MaxIdleConns: 100,
323 | IdleConnTimeout: 60 * time.Second,
324 | TLSHandshakeTimeout: 10 * time.Second,
325 | ExpectContinueTimeout: 2 * time.Second,
326 | MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1,
327 | },
328 | }
329 | }
330 |
--------------------------------------------------------------------------------
/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Tim Heckman
2 | // Use of this source code is governed by the MIT License that can be found in
3 | // the LICENSE file at the root of this repository.
4 |
5 | // Package ipdata is a client for the https://ipdata.co API. It provides
6 | // functions for looking up data, as well as parsing the data in a programmatic
7 | // way. The simplest usage is to build a new client and then use the Lookup
8 | // method.
9 | //
10 | // If you have any problems with this client, please raise an issue on GitHub:
11 | //
12 | // * https://github.com/theckman/go-ipdata/issues
13 | //
14 | // Example usage:
15 | //
16 | // import "github.com/theckman/go-ipdata"
17 | //
18 | // ipd := ipdata.NewClient("") // API key is optional
19 | // data, err := ipd.Lookup("8.8.8.8")
20 | package ipdata
21 |
--------------------------------------------------------------------------------
/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017, 2019 Tim Heckman
2 | // Use of this source code is governed by the MIT License that can be found in
3 | // the LICENSE file at the root of this repository.
4 |
5 | package ipdata
6 |
7 | // Error represents an error returned from the ipdata.co API. This error value
8 | // will be used whenever the HTTP request to the API completed, but the HTTP
9 | // status code indicated failure. The Error() method will return the JSON
10 | // message sent by the API, if present, and Code() returns the numeric HTTP
11 | // status code.
12 | type Error struct {
13 | m string
14 | c int
15 | i int
16 | }
17 |
18 | func newError(message string, code int) Error {
19 | return Error{
20 | m: message,
21 | c: code,
22 | i: -1,
23 | }
24 | }
25 |
26 | // Error returns the message JSON field sent from the ipdata.co API. This also
27 | // satisfies the error interface.
28 | func (e Error) Error() string {
29 | return e.m
30 | }
31 |
32 | // Code returns the HTTP Status code returned from the ipdata.co API.
33 | func (e Error) Code() int {
34 | return e.c
35 | }
36 |
37 | // Index returns the index first item in a BulkLookup that encountered an error.
38 | func (e Error) Index() int {
39 | return e.i
40 | }
41 |
--------------------------------------------------------------------------------
/errors_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019 Tim Heckman
2 | // Use of this source code is governed by the MIT License that can be found in
3 | // the LICENSE file at the root of this repository.
4 |
5 | package ipdata
6 |
7 | import "testing"
8 |
9 | func TestError(t *testing.T) {
10 | const wantStr = "test"
11 | const wantCode = 42
12 | const wantIndex = 84
13 |
14 | ev := Error{
15 | m: "test",
16 | c: 42,
17 | i: 84,
18 | }
19 |
20 | if got := ev.Error(); got != wantStr {
21 | t.Fatalf("ev.Error() = %q, want %q", got, wantStr)
22 | }
23 |
24 | if got := ev.Code(); got != wantCode {
25 | t.Fatalf("ev.Code() = %d, want %d", got, wantCode)
26 | }
27 |
28 | if got := ev.Index(); got != wantIndex {
29 | t.Fatalf("ev.Index() = %d, want %d", got, wantIndex)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/types.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017, 2019 Tim Heckman
2 | // Use of this source code is governed by the MIT License that can be found in
3 | // the LICENSE file at the root of this repository.
4 |
5 | package ipdata
6 |
7 | // IP is a struct that represents the JSON response from the https://ipdata.co
8 | // API.
9 | type IP struct {
10 | IP string `json:"ip"`
11 | ASN ASN `json:"asn"`
12 | Organization string `json:"organisation"`
13 |
14 | City string `json:"city"`
15 | Region string `json:"region"`
16 | RegionCode string `json:"region_code"`
17 | Postal string `json:"postal"`
18 |
19 | CountryName string `json:"country_name"`
20 | CountryCode string `json:"country_code"`
21 |
22 | Flag string `json:"flag"`
23 | EmojiFlag string `json:"emoji_flag"`
24 | EmojiUnicode string `json:"emoji_unicode"`
25 |
26 | ContinentName string `json:"continent_name"`
27 | ContinentCode string `json:"continent_code"`
28 |
29 | Latitude float64 `json:"latitude"`
30 | Longitude float64 `json:"longitude"`
31 |
32 | CallingCode string `json:"calling_code"`
33 |
34 | IsEU bool `json:"is_eu"`
35 |
36 | Languages []Language `json:"language,omitempty"`
37 |
38 | Currency *Currency `json:"currency,omitempty"`
39 |
40 | Carrier *Carrier `json:"carrier,omitempty"`
41 |
42 | TimeZone *TimeZone `json:"time_zone,omitempty"`
43 |
44 | Threat *Threat `json:"threat,omitempty"`
45 | }
46 |
47 | func (ip IP) String() string {
48 | return ip.IP
49 | }
50 |
51 | // ASN represents the Autonomous System Number data returned from the API.
52 | type ASN struct {
53 | ASN string `json:"asn"`
54 | Name string `json:"name"`
55 | Domain string `json:"domain"`
56 | Route string `json:"route"`
57 | Type string `json:"type"`
58 | }
59 |
60 | // Carrier represents the carrier data returned from the API.
61 | type Carrier struct {
62 | Name string `json:"name"`
63 | MCC string `json:"mcc"`
64 | MNC string `json:"mnc"`
65 | }
66 |
67 | // Language represents the language object within the JSON response from the
68 | // API. This provides information about the language(s) where that IP resides.
69 | type Language struct {
70 | Name string `json:"name"`
71 | Native string `json:"native"`
72 | }
73 |
74 | // Currency represents the currency object within the JSON response from the
75 | // API. This provides information about the currency where that IP resides.
76 | type Currency struct {
77 | Name string `json:"name"`
78 | Code string `json:"code"`
79 | Symbol string `json:"symbol"`
80 | Native string `json:"native"`
81 | Plural string `json:"plural"`
82 | }
83 |
84 | // TimeZone represents the time_zone object within the JSON response from the
85 | // API. This provides information about the timezone where that IP resides.
86 | type TimeZone struct {
87 | Name string `json:"name"`
88 | Abbreviation string `json:"abbr"`
89 | Offset string `json:"offset"`
90 | IsDST bool `json:"is_dst"`
91 | CurrentTime string `json:"current_time,omitempty"`
92 | }
93 |
94 | // Threat represents the threat object within the JSON response from the API.
95 | // This provides information about what type of threat this IP may be.
96 | type Threat struct {
97 | IsTOR bool `json:"is_tor"`
98 | IsVPN bool `json:"is_vpn"`
99 | IsICloudRelay bool `json:"is_icloud_relay"`
100 | IsProxy bool `json:"is_proxy"`
101 | IsDatacenter bool `json:"is_datacenter"`
102 | IsAnonymous bool `json:"is_anonymous"`
103 | IsKnownAttacker bool `json:"is_known_attacker"`
104 | IsKnownAbuser bool `json:"is_known_abuser"`
105 | IsThreat bool `json:"is_threat"`
106 | IsBogon bool `json:"is_bogon"`
107 | Blocklists []Blocklist `json:"blocklists"`
108 | Scores Scores `json:"scores"`
109 | }
110 |
111 | type Blocklist struct {
112 | Name string `json:"name"`
113 | Site string `json:"site"`
114 | Type string `json:"type"`
115 | }
116 |
117 |
118 | type Scores struct {
119 | VPNScore int `json:"vpn_score"`
120 | ProxyScore int `json:"proxy_score"`
121 | ThreatScore int `json:"threat_score"`
122 | TrustScore int `json:"trust_score"`
123 | }
124 |
125 | type bulkIP struct {
126 | IP string `json:"ip"`
127 | ASN ASN `json:"asn"`
128 | Organization string `json:"organisation"`
129 |
130 | City string `json:"city"`
131 | Region string `json:"region"`
132 | Postal string `json:"postal"`
133 |
134 | CountryName string `json:"country_name"`
135 | CountryCode string `json:"country_code"`
136 |
137 | Flag string `json:"flag"`
138 | EmojiFlag string `json:"emoji_flag"`
139 | EmojiUnicode string `json:"emoji_unicode"`
140 |
141 | ContinentName string `json:"continent_name"`
142 | ContinentCode string `json:"continent_code"`
143 |
144 | Latitude float64 `json:"latitude"`
145 | Longitude float64 `json:"longitude"`
146 |
147 | CallingCode string `json:"calling_code"`
148 |
149 | IsEU bool `json:"is_eu"`
150 |
151 | Languages []Language `json:"language,omitempty"`
152 |
153 | Currency *Currency `json:"currency,omitempty"`
154 |
155 | TimeZone *TimeZone `json:"time_zone,omitempty"`
156 |
157 | Threat *Threat `json:"threat,omitempty"`
158 |
159 | Message string `json:"message"`
160 | }
161 |
162 | func bulkToIP(bip bulkIP) *IP {
163 | ip := IP{
164 | IP: bip.IP,
165 | ASN: bip.ASN,
166 | Organization: bip.Organization,
167 | City: bip.City,
168 | Region: bip.Region,
169 | Postal: bip.Postal,
170 | CountryName: bip.CountryName,
171 | CountryCode: bip.CountryCode,
172 | Flag: bip.Flag,
173 | EmojiFlag: bip.EmojiFlag,
174 | EmojiUnicode: bip.EmojiUnicode,
175 | ContinentName: bip.ContinentName,
176 | ContinentCode: bip.ContinentCode,
177 | Latitude: bip.Latitude,
178 | Longitude: bip.Longitude,
179 | CallingCode: bip.CallingCode,
180 | IsEU: bip.IsEU,
181 | Currency: bip.Currency,
182 | TimeZone: bip.TimeZone,
183 | Threat: bip.Threat,
184 | }
185 |
186 | if len(bip.Languages) > 0 {
187 | ip.Languages = bip.Languages
188 | }
189 |
190 | return &ip
191 | }
192 |
--------------------------------------------------------------------------------
/types_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Tim Heckman
2 | // Use of this source code is governed by the MIT License that can be found in
3 | // the LICENSE file at the root of this repository.
4 |
5 | package ipdata
6 |
7 | import (
8 | "testing"
9 |
10 | "github.com/google/go-cmp/cmp"
11 | )
12 |
13 | func Test_IP_String(t *testing.T) {
14 | ip := IP{IP: "8.8.8.8"}
15 | if ip.String() != "8.8.8.8" {
16 | t.Errorf("ip.String() = %q, want %q", ip.String(), "8.8.8.8")
17 | }
18 | }
19 |
20 | func Test_bulkToIP(t *testing.T) {
21 | bip := bulkIP{
22 | IP: "76.14.47.42",
23 | ASN: ASN{
24 | ASN: "AS11404",
25 | Name: "vanoppen.biz LLC",
26 | Domain: "wavebroadband.com",
27 | Route: "76.14.0.0/17",
28 | Type: "isp",
29 | },
30 | Organization: "vanoppen.biz LLC",
31 | City: "San Francisco",
32 | Region: "California",
33 | Postal: "94132",
34 | CountryName: "United States",
35 | CountryCode: "US",
36 | Flag: tjFlagURL,
37 | EmojiUnicode: `U+1F1FA U+1F1F8`,
38 | ContinentName: "North America",
39 | ContinentCode: "NA",
40 | Latitude: 37.723,
41 | Longitude: -122.4842,
42 | CallingCode: "1",
43 | IsEU: true,
44 | Languages: []Language{{Name: "English (US)", Native: "en-us"}},
45 | Currency: &Currency{
46 | Name: "US Dollar",
47 | Code: "USD",
48 | Symbol: "$",
49 | Native: "$",
50 | Plural: "US dollars",
51 | },
52 | TimeZone: &TimeZone{
53 | Name: "America/Los_Angeles",
54 | Abbreviation: "PST",
55 | Offset: "-0800",
56 | IsDST: false,
57 | CurrentTime: "2019-02-27T15:00:32.745936-08:00",
58 | },
59 | Threat: &Threat{
60 | IsTOR: false,
61 | IsProxy: false,
62 | IsAnonymous: false,
63 | IsKnownAttacker: false,
64 | IsKnownAbuser: false,
65 | IsThreat: true,
66 | IsBogon: false,
67 | },
68 | }
69 |
70 | ip := &IP{
71 | IP: "76.14.47.42",
72 | ASN: ASN{
73 | ASN: "AS11404",
74 | Name: "vanoppen.biz LLC",
75 | Domain: "wavebroadband.com",
76 | Route: "76.14.0.0/17",
77 | Type: "isp",
78 | },
79 | Organization: "vanoppen.biz LLC",
80 | City: "San Francisco",
81 | Region: "California",
82 | Postal: "94132",
83 | CountryName: "United States",
84 | CountryCode: "US",
85 | Flag: tjFlagURL,
86 | EmojiUnicode: `U+1F1FA U+1F1F8`,
87 | ContinentName: "North America",
88 | ContinentCode: "NA",
89 | Latitude: 37.723,
90 | Longitude: -122.4842,
91 | CallingCode: "1",
92 | IsEU: true,
93 | Languages: []Language{{Name: "English (US)", Native: "en-us"}},
94 | Currency: &Currency{
95 | Name: "US Dollar",
96 | Code: "USD",
97 | Symbol: "$",
98 | Native: "$",
99 | Plural: "US dollars",
100 | },
101 | TimeZone: &TimeZone{
102 | Name: "America/Los_Angeles",
103 | Abbreviation: "PST",
104 | Offset: "-0800",
105 | IsDST: false,
106 | CurrentTime: "2019-02-27T15:00:32.745936-08:00",
107 | },
108 | Threat: &Threat{
109 | IsTOR: false,
110 | IsProxy: false,
111 | IsAnonymous: false,
112 | IsKnownAttacker: false,
113 | IsKnownAbuser: false,
114 | IsThreat: true,
115 | IsBogon: false,
116 | },
117 | }
118 |
119 | got := bulkToIP(bip)
120 | if diff := cmp.Diff(ip, got); diff != "" {
121 | t.Fatalf("IP differs: (-want +got)\n%s", diff)
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: go
3 | matrix:
4 | include:
5 | - go: 1.8.x
6 | script:
7 | - go test -v -race ./...
8 | - go: 1.9.x
9 | script:
10 | - go test -v -race ./...
11 | - go: 1.10.x
12 | script:
13 | - go test -v -race ./...
14 | - go: 1.11.x
15 | script:
16 | - go test -v -race ./...
17 | - go: 1.12.x
18 | script:
19 | - diff -u <(echo -n) <(gofmt -d .)
20 | - go test -v -race ./...
21 | - go: master
22 | script:
23 | - go test -v -race ./...
24 | allow_failures:
25 | - go: master
26 | fast_finish: true
27 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 The Go Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/README.md:
--------------------------------------------------------------------------------
1 | # Package for equality of Go values
2 |
3 | [][godoc]
4 | [][travis]
5 |
6 | This package is intended to be a more powerful and safer alternative to
7 | `reflect.DeepEqual` for comparing whether two values are semantically equal.
8 |
9 | The primary features of `cmp` are:
10 |
11 | * When the default behavior of equality does not suit the needs of the test,
12 | custom equality functions can override the equality operation.
13 | For example, an equality function may report floats as equal so long as they
14 | are within some tolerance of each other.
15 |
16 | * Types that have an `Equal` method may use that method to determine equality.
17 | This allows package authors to determine the equality operation for the types
18 | that they define.
19 |
20 | * If no custom equality functions are used and no `Equal` method is defined,
21 | equality is determined by recursively comparing the primitive kinds on both
22 | values, much like `reflect.DeepEqual`. Unlike `reflect.DeepEqual`, unexported
23 | fields are not compared by default; they result in panics unless suppressed
24 | by using an `Ignore` option (see `cmpopts.IgnoreUnexported`) or explicitly
25 | compared using the `AllowUnexported` option.
26 |
27 | See the [GoDoc documentation][godoc] for more information.
28 |
29 | This is not an official Google product.
30 |
31 | [godoc]: https://godoc.org/github.com/google/go-cmp/cmp
32 | [travis]: https://travis-ci.org/google/go-cmp
33 |
34 | ## Install
35 |
36 | ```
37 | go get -u github.com/google/go-cmp/cmp
38 | ```
39 |
40 | ## License
41 |
42 | BSD - See [LICENSE][license] file
43 |
44 | [license]: https://github.com/google/go-cmp/blob/master/LICENSE
45 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // Package cmpopts provides common options for the cmp package.
6 | package cmpopts
7 |
8 | import (
9 | "math"
10 | "reflect"
11 |
12 | "github.com/google/go-cmp/cmp"
13 | )
14 |
15 | func equateAlways(_, _ interface{}) bool { return true }
16 |
17 | // EquateEmpty returns a Comparer option that determines all maps and slices
18 | // with a length of zero to be equal, regardless of whether they are nil.
19 | //
20 | // EquateEmpty can be used in conjunction with SortSlices and SortMaps.
21 | func EquateEmpty() cmp.Option {
22 | return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways))
23 | }
24 |
25 | func isEmpty(x, y interface{}) bool {
26 | vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
27 | return (x != nil && y != nil && vx.Type() == vy.Type()) &&
28 | (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) &&
29 | (vx.Len() == 0 && vy.Len() == 0)
30 | }
31 |
32 | // EquateApprox returns a Comparer option that determines float32 or float64
33 | // values to be equal if they are within a relative fraction or absolute margin.
34 | // This option is not used when either x or y is NaN or infinite.
35 | //
36 | // The fraction determines that the difference of two values must be within the
37 | // smaller fraction of the two values, while the margin determines that the two
38 | // values must be within some absolute margin.
39 | // To express only a fraction or only a margin, use 0 for the other parameter.
40 | // The fraction and margin must be non-negative.
41 | //
42 | // The mathematical expression used is equivalent to:
43 | // |x-y| ≤ max(fraction*min(|x|, |y|), margin)
44 | //
45 | // EquateApprox can be used in conjunction with EquateNaNs.
46 | func EquateApprox(fraction, margin float64) cmp.Option {
47 | if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) {
48 | panic("margin or fraction must be a non-negative number")
49 | }
50 | a := approximator{fraction, margin}
51 | return cmp.Options{
52 | cmp.FilterValues(areRealF64s, cmp.Comparer(a.compareF64)),
53 | cmp.FilterValues(areRealF32s, cmp.Comparer(a.compareF32)),
54 | }
55 | }
56 |
57 | type approximator struct{ frac, marg float64 }
58 |
59 | func areRealF64s(x, y float64) bool {
60 | return !math.IsNaN(x) && !math.IsNaN(y) && !math.IsInf(x, 0) && !math.IsInf(y, 0)
61 | }
62 | func areRealF32s(x, y float32) bool {
63 | return areRealF64s(float64(x), float64(y))
64 | }
65 | func (a approximator) compareF64(x, y float64) bool {
66 | relMarg := a.frac * math.Min(math.Abs(x), math.Abs(y))
67 | return math.Abs(x-y) <= math.Max(a.marg, relMarg)
68 | }
69 | func (a approximator) compareF32(x, y float32) bool {
70 | return a.compareF64(float64(x), float64(y))
71 | }
72 |
73 | // EquateNaNs returns a Comparer option that determines float32 and float64
74 | // NaN values to be equal.
75 | //
76 | // EquateNaNs can be used in conjunction with EquateApprox.
77 | func EquateNaNs() cmp.Option {
78 | return cmp.Options{
79 | cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)),
80 | cmp.FilterValues(areNaNsF32s, cmp.Comparer(equateAlways)),
81 | }
82 | }
83 |
84 | func areNaNsF64s(x, y float64) bool {
85 | return math.IsNaN(x) && math.IsNaN(y)
86 | }
87 | func areNaNsF32s(x, y float32) bool {
88 | return areNaNsF64s(float64(x), float64(y))
89 | }
90 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmpopts
6 |
7 | import (
8 | "fmt"
9 | "reflect"
10 | "unicode"
11 | "unicode/utf8"
12 |
13 | "github.com/google/go-cmp/cmp"
14 | "github.com/google/go-cmp/cmp/internal/function"
15 | )
16 |
17 | // IgnoreFields returns an Option that ignores exported fields of the
18 | // given names on a single struct type.
19 | // The struct type is specified by passing in a value of that type.
20 | //
21 | // The name may be a dot-delimited string (e.g., "Foo.Bar") to ignore a
22 | // specific sub-field that is embedded or nested within the parent struct.
23 | //
24 | // This does not handle unexported fields; use IgnoreUnexported instead.
25 | func IgnoreFields(typ interface{}, names ...string) cmp.Option {
26 | sf := newStructFilter(typ, names...)
27 | return cmp.FilterPath(sf.filter, cmp.Ignore())
28 | }
29 |
30 | // IgnoreTypes returns an Option that ignores all values assignable to
31 | // certain types, which are specified by passing in a value of each type.
32 | func IgnoreTypes(typs ...interface{}) cmp.Option {
33 | tf := newTypeFilter(typs...)
34 | return cmp.FilterPath(tf.filter, cmp.Ignore())
35 | }
36 |
37 | type typeFilter []reflect.Type
38 |
39 | func newTypeFilter(typs ...interface{}) (tf typeFilter) {
40 | for _, typ := range typs {
41 | t := reflect.TypeOf(typ)
42 | if t == nil {
43 | // This occurs if someone tries to pass in sync.Locker(nil)
44 | panic("cannot determine type; consider using IgnoreInterfaces")
45 | }
46 | tf = append(tf, t)
47 | }
48 | return tf
49 | }
50 | func (tf typeFilter) filter(p cmp.Path) bool {
51 | if len(p) < 1 {
52 | return false
53 | }
54 | t := p.Last().Type()
55 | for _, ti := range tf {
56 | if t.AssignableTo(ti) {
57 | return true
58 | }
59 | }
60 | return false
61 | }
62 |
63 | // IgnoreInterfaces returns an Option that ignores all values or references of
64 | // values assignable to certain interface types. These interfaces are specified
65 | // by passing in an anonymous struct with the interface types embedded in it.
66 | // For example, to ignore sync.Locker, pass in struct{sync.Locker}{}.
67 | func IgnoreInterfaces(ifaces interface{}) cmp.Option {
68 | tf := newIfaceFilter(ifaces)
69 | return cmp.FilterPath(tf.filter, cmp.Ignore())
70 | }
71 |
72 | type ifaceFilter []reflect.Type
73 |
74 | func newIfaceFilter(ifaces interface{}) (tf ifaceFilter) {
75 | t := reflect.TypeOf(ifaces)
76 | if ifaces == nil || t.Name() != "" || t.Kind() != reflect.Struct {
77 | panic("input must be an anonymous struct")
78 | }
79 | for i := 0; i < t.NumField(); i++ {
80 | fi := t.Field(i)
81 | switch {
82 | case !fi.Anonymous:
83 | panic("struct cannot have named fields")
84 | case fi.Type.Kind() != reflect.Interface:
85 | panic("embedded field must be an interface type")
86 | case fi.Type.NumMethod() == 0:
87 | // This matches everything; why would you ever want this?
88 | panic("cannot ignore empty interface")
89 | default:
90 | tf = append(tf, fi.Type)
91 | }
92 | }
93 | return tf
94 | }
95 | func (tf ifaceFilter) filter(p cmp.Path) bool {
96 | if len(p) < 1 {
97 | return false
98 | }
99 | t := p.Last().Type()
100 | for _, ti := range tf {
101 | if t.AssignableTo(ti) {
102 | return true
103 | }
104 | if t.Kind() != reflect.Ptr && reflect.PtrTo(t).AssignableTo(ti) {
105 | return true
106 | }
107 | }
108 | return false
109 | }
110 |
111 | // IgnoreUnexported returns an Option that only ignores the immediate unexported
112 | // fields of a struct, including anonymous fields of unexported types.
113 | // In particular, unexported fields within the struct's exported fields
114 | // of struct types, including anonymous fields, will not be ignored unless the
115 | // type of the field itself is also passed to IgnoreUnexported.
116 | //
117 | // Avoid ignoring unexported fields of a type which you do not control (i.e. a
118 | // type from another repository), as changes to the implementation of such types
119 | // may change how the comparison behaves. Prefer a custom Comparer instead.
120 | func IgnoreUnexported(typs ...interface{}) cmp.Option {
121 | ux := newUnexportedFilter(typs...)
122 | return cmp.FilterPath(ux.filter, cmp.Ignore())
123 | }
124 |
125 | type unexportedFilter struct{ m map[reflect.Type]bool }
126 |
127 | func newUnexportedFilter(typs ...interface{}) unexportedFilter {
128 | ux := unexportedFilter{m: make(map[reflect.Type]bool)}
129 | for _, typ := range typs {
130 | t := reflect.TypeOf(typ)
131 | if t == nil || t.Kind() != reflect.Struct {
132 | panic(fmt.Sprintf("invalid struct type: %T", typ))
133 | }
134 | ux.m[t] = true
135 | }
136 | return ux
137 | }
138 | func (xf unexportedFilter) filter(p cmp.Path) bool {
139 | sf, ok := p.Index(-1).(cmp.StructField)
140 | if !ok {
141 | return false
142 | }
143 | return xf.m[p.Index(-2).Type()] && !isExported(sf.Name())
144 | }
145 |
146 | // isExported reports whether the identifier is exported.
147 | func isExported(id string) bool {
148 | r, _ := utf8.DecodeRuneInString(id)
149 | return unicode.IsUpper(r)
150 | }
151 |
152 | // IgnoreSliceElements returns an Option that ignores elements of []V.
153 | // The discard function must be of the form "func(T) bool" which is used to
154 | // ignore slice elements of type V, where V is assignable to T.
155 | // Elements are ignored if the function reports true.
156 | func IgnoreSliceElements(discardFunc interface{}) cmp.Option {
157 | vf := reflect.ValueOf(discardFunc)
158 | if !function.IsType(vf.Type(), function.ValuePredicate) || vf.IsNil() {
159 | panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
160 | }
161 | return cmp.FilterPath(func(p cmp.Path) bool {
162 | si, ok := p.Index(-1).(cmp.SliceIndex)
163 | if !ok {
164 | return false
165 | }
166 | if !si.Type().AssignableTo(vf.Type().In(0)) {
167 | return false
168 | }
169 | vx, vy := si.Values()
170 | if vx.IsValid() && vf.Call([]reflect.Value{vx})[0].Bool() {
171 | return true
172 | }
173 | if vy.IsValid() && vf.Call([]reflect.Value{vy})[0].Bool() {
174 | return true
175 | }
176 | return false
177 | }, cmp.Ignore())
178 | }
179 |
180 | // IgnoreMapEntries returns an Option that ignores entries of map[K]V.
181 | // The discard function must be of the form "func(T, R) bool" which is used to
182 | // ignore map entries of type K and V, where K and V are assignable to T and R.
183 | // Entries are ignored if the function reports true.
184 | func IgnoreMapEntries(discardFunc interface{}) cmp.Option {
185 | vf := reflect.ValueOf(discardFunc)
186 | if !function.IsType(vf.Type(), function.KeyValuePredicate) || vf.IsNil() {
187 | panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
188 | }
189 | return cmp.FilterPath(func(p cmp.Path) bool {
190 | mi, ok := p.Index(-1).(cmp.MapIndex)
191 | if !ok {
192 | return false
193 | }
194 | if !mi.Key().Type().AssignableTo(vf.Type().In(0)) || !mi.Type().AssignableTo(vf.Type().In(1)) {
195 | return false
196 | }
197 | k := mi.Key()
198 | vx, vy := mi.Values()
199 | if vx.IsValid() && vf.Call([]reflect.Value{k, vx})[0].Bool() {
200 | return true
201 | }
202 | if vy.IsValid() && vf.Call([]reflect.Value{k, vy})[0].Bool() {
203 | return true
204 | }
205 | return false
206 | }, cmp.Ignore())
207 | }
208 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmpopts
6 |
7 | import (
8 | "fmt"
9 | "reflect"
10 | "sort"
11 |
12 | "github.com/google/go-cmp/cmp"
13 | "github.com/google/go-cmp/cmp/internal/function"
14 | )
15 |
16 | // SortSlices returns a Transformer option that sorts all []V.
17 | // The less function must be of the form "func(T, T) bool" which is used to
18 | // sort any slice with element type V that is assignable to T.
19 | //
20 | // The less function must be:
21 | // • Deterministic: less(x, y) == less(x, y)
22 | // • Irreflexive: !less(x, x)
23 | // • Transitive: if !less(x, y) and !less(y, z), then !less(x, z)
24 | //
25 | // The less function does not have to be "total". That is, if !less(x, y) and
26 | // !less(y, x) for two elements x and y, their relative order is maintained.
27 | //
28 | // SortSlices can be used in conjunction with EquateEmpty.
29 | func SortSlices(lessFunc interface{}) cmp.Option {
30 | vf := reflect.ValueOf(lessFunc)
31 | if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
32 | panic(fmt.Sprintf("invalid less function: %T", lessFunc))
33 | }
34 | ss := sliceSorter{vf.Type().In(0), vf}
35 | return cmp.FilterValues(ss.filter, cmp.Transformer("cmpopts.SortSlices", ss.sort))
36 | }
37 |
38 | type sliceSorter struct {
39 | in reflect.Type // T
40 | fnc reflect.Value // func(T, T) bool
41 | }
42 |
43 | func (ss sliceSorter) filter(x, y interface{}) bool {
44 | vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
45 | if !(x != nil && y != nil && vx.Type() == vy.Type()) ||
46 | !(vx.Kind() == reflect.Slice && vx.Type().Elem().AssignableTo(ss.in)) ||
47 | (vx.Len() <= 1 && vy.Len() <= 1) {
48 | return false
49 | }
50 | // Check whether the slices are already sorted to avoid an infinite
51 | // recursion cycle applying the same transform to itself.
52 | ok1 := sort.SliceIsSorted(x, func(i, j int) bool { return ss.less(vx, i, j) })
53 | ok2 := sort.SliceIsSorted(y, func(i, j int) bool { return ss.less(vy, i, j) })
54 | return !ok1 || !ok2
55 | }
56 | func (ss sliceSorter) sort(x interface{}) interface{} {
57 | src := reflect.ValueOf(x)
58 | dst := reflect.MakeSlice(src.Type(), src.Len(), src.Len())
59 | for i := 0; i < src.Len(); i++ {
60 | dst.Index(i).Set(src.Index(i))
61 | }
62 | sort.SliceStable(dst.Interface(), func(i, j int) bool { return ss.less(dst, i, j) })
63 | ss.checkSort(dst)
64 | return dst.Interface()
65 | }
66 | func (ss sliceSorter) checkSort(v reflect.Value) {
67 | start := -1 // Start of a sequence of equal elements.
68 | for i := 1; i < v.Len(); i++ {
69 | if ss.less(v, i-1, i) {
70 | // Check that first and last elements in v[start:i] are equal.
71 | if start >= 0 && (ss.less(v, start, i-1) || ss.less(v, i-1, start)) {
72 | panic(fmt.Sprintf("incomparable values detected: want equal elements: %v", v.Slice(start, i)))
73 | }
74 | start = -1
75 | } else if start == -1 {
76 | start = i
77 | }
78 | }
79 | }
80 | func (ss sliceSorter) less(v reflect.Value, i, j int) bool {
81 | vx, vy := v.Index(i), v.Index(j)
82 | return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool()
83 | }
84 |
85 | // SortMaps returns a Transformer option that flattens map[K]V types to be a
86 | // sorted []struct{K, V}. The less function must be of the form
87 | // "func(T, T) bool" which is used to sort any map with key K that is
88 | // assignable to T.
89 | //
90 | // Flattening the map into a slice has the property that cmp.Equal is able to
91 | // use Comparers on K or the K.Equal method if it exists.
92 | //
93 | // The less function must be:
94 | // • Deterministic: less(x, y) == less(x, y)
95 | // • Irreflexive: !less(x, x)
96 | // • Transitive: if !less(x, y) and !less(y, z), then !less(x, z)
97 | // • Total: if x != y, then either less(x, y) or less(y, x)
98 | //
99 | // SortMaps can be used in conjunction with EquateEmpty.
100 | func SortMaps(lessFunc interface{}) cmp.Option {
101 | vf := reflect.ValueOf(lessFunc)
102 | if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
103 | panic(fmt.Sprintf("invalid less function: %T", lessFunc))
104 | }
105 | ms := mapSorter{vf.Type().In(0), vf}
106 | return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort))
107 | }
108 |
109 | type mapSorter struct {
110 | in reflect.Type // T
111 | fnc reflect.Value // func(T, T) bool
112 | }
113 |
114 | func (ms mapSorter) filter(x, y interface{}) bool {
115 | vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
116 | return (x != nil && y != nil && vx.Type() == vy.Type()) &&
117 | (vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) &&
118 | (vx.Len() != 0 || vy.Len() != 0)
119 | }
120 | func (ms mapSorter) sort(x interface{}) interface{} {
121 | src := reflect.ValueOf(x)
122 | outType := reflect.StructOf([]reflect.StructField{
123 | {Name: "K", Type: src.Type().Key()},
124 | {Name: "V", Type: src.Type().Elem()},
125 | })
126 | dst := reflect.MakeSlice(reflect.SliceOf(outType), src.Len(), src.Len())
127 | for i, k := range src.MapKeys() {
128 | v := reflect.New(outType).Elem()
129 | v.Field(0).Set(k)
130 | v.Field(1).Set(src.MapIndex(k))
131 | dst.Index(i).Set(v)
132 | }
133 | sort.Slice(dst.Interface(), func(i, j int) bool { return ms.less(dst, i, j) })
134 | ms.checkSort(dst)
135 | return dst.Interface()
136 | }
137 | func (ms mapSorter) checkSort(v reflect.Value) {
138 | for i := 1; i < v.Len(); i++ {
139 | if !ms.less(v, i-1, i) {
140 | panic(fmt.Sprintf("partial order detected: want %v < %v", v.Index(i-1), v.Index(i)))
141 | }
142 | }
143 | }
144 | func (ms mapSorter) less(v reflect.Value, i, j int) bool {
145 | vx, vy := v.Index(i).Field(0), v.Index(j).Field(0)
146 | return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool()
147 | }
148 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmpopts
6 |
7 | import (
8 | "fmt"
9 | "reflect"
10 | "strings"
11 |
12 | "github.com/google/go-cmp/cmp"
13 | )
14 |
15 | // filterField returns a new Option where opt is only evaluated on paths that
16 | // include a specific exported field on a single struct type.
17 | // The struct type is specified by passing in a value of that type.
18 | //
19 | // The name may be a dot-delimited string (e.g., "Foo.Bar") to select a
20 | // specific sub-field that is embedded or nested within the parent struct.
21 | func filterField(typ interface{}, name string, opt cmp.Option) cmp.Option {
22 | // TODO: This is currently unexported over concerns of how helper filters
23 | // can be composed together easily.
24 | // TODO: Add tests for FilterField.
25 |
26 | sf := newStructFilter(typ, name)
27 | return cmp.FilterPath(sf.filter, opt)
28 | }
29 |
30 | type structFilter struct {
31 | t reflect.Type // The root struct type to match on
32 | ft fieldTree // Tree of fields to match on
33 | }
34 |
35 | func newStructFilter(typ interface{}, names ...string) structFilter {
36 | // TODO: Perhaps allow * as a special identifier to allow ignoring any
37 | // number of path steps until the next field match?
38 | // This could be useful when a concrete struct gets transformed into
39 | // an anonymous struct where it is not possible to specify that by type,
40 | // but the transformer happens to provide guarantees about the names of
41 | // the transformed fields.
42 |
43 | t := reflect.TypeOf(typ)
44 | if t == nil || t.Kind() != reflect.Struct {
45 | panic(fmt.Sprintf("%T must be a struct", typ))
46 | }
47 | var ft fieldTree
48 | for _, name := range names {
49 | cname, err := canonicalName(t, name)
50 | if err != nil {
51 | panic(fmt.Sprintf("%s: %v", strings.Join(cname, "."), err))
52 | }
53 | ft.insert(cname)
54 | }
55 | return structFilter{t, ft}
56 | }
57 |
58 | func (sf structFilter) filter(p cmp.Path) bool {
59 | for i, ps := range p {
60 | if ps.Type().AssignableTo(sf.t) && sf.ft.matchPrefix(p[i+1:]) {
61 | return true
62 | }
63 | }
64 | return false
65 | }
66 |
67 | // fieldTree represents a set of dot-separated identifiers.
68 | //
69 | // For example, inserting the following selectors:
70 | // Foo
71 | // Foo.Bar.Baz
72 | // Foo.Buzz
73 | // Nuka.Cola.Quantum
74 | //
75 | // Results in a tree of the form:
76 | // {sub: {
77 | // "Foo": {ok: true, sub: {
78 | // "Bar": {sub: {
79 | // "Baz": {ok: true},
80 | // }},
81 | // "Buzz": {ok: true},
82 | // }},
83 | // "Nuka": {sub: {
84 | // "Cola": {sub: {
85 | // "Quantum": {ok: true},
86 | // }},
87 | // }},
88 | // }}
89 | type fieldTree struct {
90 | ok bool // Whether this is a specified node
91 | sub map[string]fieldTree // The sub-tree of fields under this node
92 | }
93 |
94 | // insert inserts a sequence of field accesses into the tree.
95 | func (ft *fieldTree) insert(cname []string) {
96 | if ft.sub == nil {
97 | ft.sub = make(map[string]fieldTree)
98 | }
99 | if len(cname) == 0 {
100 | ft.ok = true
101 | return
102 | }
103 | sub := ft.sub[cname[0]]
104 | sub.insert(cname[1:])
105 | ft.sub[cname[0]] = sub
106 | }
107 |
108 | // matchPrefix reports whether any selector in the fieldTree matches
109 | // the start of path p.
110 | func (ft fieldTree) matchPrefix(p cmp.Path) bool {
111 | for _, ps := range p {
112 | switch ps := ps.(type) {
113 | case cmp.StructField:
114 | ft = ft.sub[ps.Name()]
115 | if ft.ok {
116 | return true
117 | }
118 | if len(ft.sub) == 0 {
119 | return false
120 | }
121 | case cmp.Indirect:
122 | default:
123 | return false
124 | }
125 | }
126 | return false
127 | }
128 |
129 | // canonicalName returns a list of identifiers where any struct field access
130 | // through an embedded field is expanded to include the names of the embedded
131 | // types themselves.
132 | //
133 | // For example, suppose field "Foo" is not directly in the parent struct,
134 | // but actually from an embedded struct of type "Bar". Then, the canonical name
135 | // of "Foo" is actually "Bar.Foo".
136 | //
137 | // Suppose field "Foo" is not directly in the parent struct, but actually
138 | // a field in two different embedded structs of types "Bar" and "Baz".
139 | // Then the selector "Foo" causes a panic since it is ambiguous which one it
140 | // refers to. The user must specify either "Bar.Foo" or "Baz.Foo".
141 | func canonicalName(t reflect.Type, sel string) ([]string, error) {
142 | var name string
143 | sel = strings.TrimPrefix(sel, ".")
144 | if sel == "" {
145 | return nil, fmt.Errorf("name must not be empty")
146 | }
147 | if i := strings.IndexByte(sel, '.'); i < 0 {
148 | name, sel = sel, ""
149 | } else {
150 | name, sel = sel[:i], sel[i:]
151 | }
152 |
153 | // Type must be a struct or pointer to struct.
154 | if t.Kind() == reflect.Ptr {
155 | t = t.Elem()
156 | }
157 | if t.Kind() != reflect.Struct {
158 | return nil, fmt.Errorf("%v must be a struct", t)
159 | }
160 |
161 | // Find the canonical name for this current field name.
162 | // If the field exists in an embedded struct, then it will be expanded.
163 | if !isExported(name) {
164 | // Disallow unexported fields:
165 | // * To discourage people from actually touching unexported fields
166 | // * FieldByName is buggy (https://golang.org/issue/4876)
167 | return []string{name}, fmt.Errorf("name must be exported")
168 | }
169 | sf, ok := t.FieldByName(name)
170 | if !ok {
171 | return []string{name}, fmt.Errorf("does not exist")
172 | }
173 | var ss []string
174 | for i := range sf.Index {
175 | ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name)
176 | }
177 | if sel == "" {
178 | return ss, nil
179 | }
180 | ssPost, err := canonicalName(sf.Type, sel)
181 | return append(ss, ssPost...), err
182 | }
183 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmpopts
6 |
7 | import (
8 | "github.com/google/go-cmp/cmp"
9 | )
10 |
11 | type xformFilter struct{ xform cmp.Option }
12 |
13 | func (xf xformFilter) filter(p cmp.Path) bool {
14 | for _, ps := range p {
15 | if t, ok := ps.(cmp.Transform); ok && t.Option() == xf.xform {
16 | return false
17 | }
18 | }
19 | return true
20 | }
21 |
22 | // AcyclicTransformer returns a Transformer with a filter applied that ensures
23 | // that the transformer cannot be recursively applied upon its own output.
24 | //
25 | // An example use case is a transformer that splits a string by lines:
26 | // AcyclicTransformer("SplitLines", func(s string) []string{
27 | // return strings.Split(s, "\n")
28 | // })
29 | //
30 | // Had this been an unfiltered Transformer instead, this would result in an
31 | // infinite cycle converting a string to []string to [][]string and so on.
32 | func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option {
33 | xf := xformFilter{cmp.Transformer(name, xformFunc)}
34 | return cmp.FilterPath(xf.filter, xf.xform)
35 | }
36 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/example_reporter_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp_test
6 |
7 | import (
8 | "fmt"
9 | "strings"
10 |
11 | "github.com/google/go-cmp/cmp"
12 | )
13 |
14 | // DiffReporter is a simple custom reporter that only records differences
15 | // detected during comparison.
16 | type DiffReporter struct {
17 | path cmp.Path
18 | diffs []string
19 | }
20 |
21 | func (r *DiffReporter) PushStep(ps cmp.PathStep) {
22 | r.path = append(r.path, ps)
23 | }
24 |
25 | func (r *DiffReporter) Report(rs cmp.Result) {
26 | if !rs.Equal() {
27 | vx, vy := r.path.Last().Values()
28 | r.diffs = append(r.diffs, fmt.Sprintf("%#v:\n\t-: %+v\n\t+: %+v\n", r.path, vx, vy))
29 | }
30 | }
31 |
32 | func (r *DiffReporter) PopStep() {
33 | r.path = r.path[:len(r.path)-1]
34 | }
35 |
36 | func (r *DiffReporter) String() string {
37 | return strings.Join(r.diffs, "\n")
38 | }
39 |
40 | func ExampleReporter() {
41 | x, y := MakeGatewayInfo()
42 |
43 | var r DiffReporter
44 | cmp.Equal(x, y, cmp.Reporter(&r))
45 | fmt.Print(r.String())
46 |
47 | // Output:
48 | // {cmp_test.Gateway}.IPAddress:
49 | // -: 192.168.0.1
50 | // +: 192.168.0.2
51 | //
52 | // {cmp_test.Gateway}.Clients[4].IPAddress:
53 | // -: 192.168.0.219
54 | // +: 192.168.0.221
55 | //
56 | // {cmp_test.Gateway}.Clients[5->?]:
57 | // -: {Hostname:americano IPAddress:192.168.0.188 LastSeen:2009-11-10 23:03:05 +0000 UTC}
58 | // +:
59 | }
60 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/export_panic.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // +build purego
6 |
7 | package cmp
8 |
9 | import "reflect"
10 |
11 | const supportAllowUnexported = false
12 |
13 | func retrieveUnexportedField(reflect.Value, reflect.StructField) reflect.Value {
14 | panic("retrieveUnexportedField is not implemented")
15 | }
16 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/export_unsafe.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // +build !purego
6 |
7 | package cmp
8 |
9 | import (
10 | "reflect"
11 | "unsafe"
12 | )
13 |
14 | const supportAllowUnexported = true
15 |
16 | // retrieveUnexportedField uses unsafe to forcibly retrieve any field from
17 | // a struct such that the value has read-write permissions.
18 | //
19 | // The parent struct, v, must be addressable, while f must be a StructField
20 | // describing the field to retrieve.
21 | func retrieveUnexportedField(v reflect.Value, f reflect.StructField) reflect.Value {
22 | return reflect.NewAt(f.Type, unsafe.Pointer(v.UnsafeAddr()+f.Offset)).Elem()
23 | }
24 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // +build !cmp_debug
6 |
7 | package diff
8 |
9 | var debug debugger
10 |
11 | type debugger struct{}
12 |
13 | func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc {
14 | return f
15 | }
16 | func (debugger) Update() {}
17 | func (debugger) Finish() {}
18 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // +build cmp_debug
6 |
7 | package diff
8 |
9 | import (
10 | "fmt"
11 | "strings"
12 | "sync"
13 | "time"
14 | )
15 |
16 | // The algorithm can be seen running in real-time by enabling debugging:
17 | // go test -tags=cmp_debug -v
18 | //
19 | // Example output:
20 | // === RUN TestDifference/#34
21 | // ┌───────────────────────────────┐
22 | // │ \ · · · · · · · · · · · · · · │
23 | // │ · # · · · · · · · · · · · · · │
24 | // │ · \ · · · · · · · · · · · · · │
25 | // │ · · \ · · · · · · · · · · · · │
26 | // │ · · · X # · · · · · · · · · · │
27 | // │ · · · # \ · · · · · · · · · · │
28 | // │ · · · · · # # · · · · · · · · │
29 | // │ · · · · · # \ · · · · · · · · │
30 | // │ · · · · · · · \ · · · · · · · │
31 | // │ · · · · · · · · \ · · · · · · │
32 | // │ · · · · · · · · · \ · · · · · │
33 | // │ · · · · · · · · · · \ · · # · │
34 | // │ · · · · · · · · · · · \ # # · │
35 | // │ · · · · · · · · · · · # # # · │
36 | // │ · · · · · · · · · · # # # # · │
37 | // │ · · · · · · · · · # # # # # · │
38 | // │ · · · · · · · · · · · · · · \ │
39 | // └───────────────────────────────┘
40 | // [.Y..M.XY......YXYXY.|]
41 | //
42 | // The grid represents the edit-graph where the horizontal axis represents
43 | // list X and the vertical axis represents list Y. The start of the two lists
44 | // is the top-left, while the ends are the bottom-right. The '·' represents
45 | // an unexplored node in the graph. The '\' indicates that the two symbols
46 | // from list X and Y are equal. The 'X' indicates that two symbols are similar
47 | // (but not exactly equal) to each other. The '#' indicates that the two symbols
48 | // are different (and not similar). The algorithm traverses this graph trying to
49 | // make the paths starting in the top-left and the bottom-right connect.
50 | //
51 | // The series of '.', 'X', 'Y', and 'M' characters at the bottom represents
52 | // the currently established path from the forward and reverse searches,
53 | // separated by a '|' character.
54 |
55 | const (
56 | updateDelay = 100 * time.Millisecond
57 | finishDelay = 500 * time.Millisecond
58 | ansiTerminal = true // ANSI escape codes used to move terminal cursor
59 | )
60 |
61 | var debug debugger
62 |
63 | type debugger struct {
64 | sync.Mutex
65 | p1, p2 EditScript
66 | fwdPath, revPath *EditScript
67 | grid []byte
68 | lines int
69 | }
70 |
71 | func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc {
72 | dbg.Lock()
73 | dbg.fwdPath, dbg.revPath = p1, p2
74 | top := "┌─" + strings.Repeat("──", nx) + "┐\n"
75 | row := "│ " + strings.Repeat("· ", nx) + "│\n"
76 | btm := "└─" + strings.Repeat("──", nx) + "┘\n"
77 | dbg.grid = []byte(top + strings.Repeat(row, ny) + btm)
78 | dbg.lines = strings.Count(dbg.String(), "\n")
79 | fmt.Print(dbg)
80 |
81 | // Wrap the EqualFunc so that we can intercept each result.
82 | return func(ix, iy int) (r Result) {
83 | cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")]
84 | for i := range cell {
85 | cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot
86 | }
87 | switch r = f(ix, iy); {
88 | case r.Equal():
89 | cell[0] = '\\'
90 | case r.Similar():
91 | cell[0] = 'X'
92 | default:
93 | cell[0] = '#'
94 | }
95 | return
96 | }
97 | }
98 |
99 | func (dbg *debugger) Update() {
100 | dbg.print(updateDelay)
101 | }
102 |
103 | func (dbg *debugger) Finish() {
104 | dbg.print(finishDelay)
105 | dbg.Unlock()
106 | }
107 |
108 | func (dbg *debugger) String() string {
109 | dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0]
110 | for i := len(*dbg.revPath) - 1; i >= 0; i-- {
111 | dbg.p2 = append(dbg.p2, (*dbg.revPath)[i])
112 | }
113 | return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2)
114 | }
115 |
116 | func (dbg *debugger) print(d time.Duration) {
117 | if ansiTerminal {
118 | fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor
119 | }
120 | fmt.Print(dbg)
121 | time.Sleep(d)
122 | }
123 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package flags
6 |
7 | // Deterministic controls whether the output of Diff should be deterministic.
8 | // This is only used for testing.
9 | var Deterministic bool
10 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // +build !go1.10
6 |
7 | package flags
8 |
9 | // AtLeastGo110 reports whether the Go toolchain is at least Go 1.10.
10 | const AtLeastGo110 = false
11 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // +build go1.10
6 |
7 | package flags
8 |
9 | // AtLeastGo110 reports whether the Go toolchain is at least Go 1.10.
10 | const AtLeastGo110 = true
11 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/function/func.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // Package function provides functionality for identifying function types.
6 | package function
7 |
8 | import (
9 | "reflect"
10 | "regexp"
11 | "runtime"
12 | "strings"
13 | )
14 |
15 | type funcType int
16 |
17 | const (
18 | _ funcType = iota
19 |
20 | tbFunc // func(T) bool
21 | ttbFunc // func(T, T) bool
22 | trbFunc // func(T, R) bool
23 | tibFunc // func(T, I) bool
24 | trFunc // func(T) R
25 |
26 | Equal = ttbFunc // func(T, T) bool
27 | EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool
28 | Transformer = trFunc // func(T) R
29 | ValueFilter = ttbFunc // func(T, T) bool
30 | Less = ttbFunc // func(T, T) bool
31 | ValuePredicate = tbFunc // func(T) bool
32 | KeyValuePredicate = trbFunc // func(T, R) bool
33 | )
34 |
35 | var boolType = reflect.TypeOf(true)
36 |
37 | // IsType reports whether the reflect.Type is of the specified function type.
38 | func IsType(t reflect.Type, ft funcType) bool {
39 | if t == nil || t.Kind() != reflect.Func || t.IsVariadic() {
40 | return false
41 | }
42 | ni, no := t.NumIn(), t.NumOut()
43 | switch ft {
44 | case tbFunc: // func(T) bool
45 | if ni == 1 && no == 1 && t.Out(0) == boolType {
46 | return true
47 | }
48 | case ttbFunc: // func(T, T) bool
49 | if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType {
50 | return true
51 | }
52 | case trbFunc: // func(T, R) bool
53 | if ni == 2 && no == 1 && t.Out(0) == boolType {
54 | return true
55 | }
56 | case tibFunc: // func(T, I) bool
57 | if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType {
58 | return true
59 | }
60 | case trFunc: // func(T) R
61 | if ni == 1 && no == 1 {
62 | return true
63 | }
64 | }
65 | return false
66 | }
67 |
68 | var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`)
69 |
70 | // NameOf returns the name of the function value.
71 | func NameOf(v reflect.Value) string {
72 | fnc := runtime.FuncForPC(v.Pointer())
73 | if fnc == nil {
74 | return ""
75 | }
76 | fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm"
77 |
78 | // Method closures have a "-fm" suffix.
79 | fullName = strings.TrimSuffix(fullName, "-fm")
80 |
81 | var name string
82 | for len(fullName) > 0 {
83 | inParen := strings.HasSuffix(fullName, ")")
84 | fullName = strings.TrimSuffix(fullName, ")")
85 |
86 | s := lastIdentRx.FindString(fullName)
87 | if s == "" {
88 | break
89 | }
90 | name = s + "." + name
91 | fullName = strings.TrimSuffix(fullName, s)
92 |
93 | if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 {
94 | fullName = fullName[:i]
95 | }
96 | fullName = strings.TrimSuffix(fullName, ".")
97 | }
98 | return strings.TrimSuffix(name, ".")
99 | }
100 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/function/func_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package function
6 |
7 | import (
8 | "bytes"
9 | "reflect"
10 | "testing"
11 | )
12 |
13 | type myType struct{ bytes.Buffer }
14 |
15 | func (myType) valueMethod() {}
16 | func (myType) ValueMethod() {}
17 |
18 | func (*myType) pointerMethod() {}
19 | func (*myType) PointerMethod() {}
20 |
21 | func TestNameOf(t *testing.T) {
22 | tests := []struct {
23 | fnc interface{}
24 | want string
25 | }{
26 | {TestNameOf, "function.TestNameOf"},
27 | {func() {}, "function.TestNameOf.func1"},
28 | {(myType).valueMethod, "function.myType.valueMethod"},
29 | {(myType).ValueMethod, "function.myType.ValueMethod"},
30 | {(myType{}).valueMethod, "function.myType.valueMethod"},
31 | {(myType{}).ValueMethod, "function.myType.ValueMethod"},
32 | {(*myType).valueMethod, "function.myType.valueMethod"},
33 | {(*myType).ValueMethod, "function.myType.ValueMethod"},
34 | {(&myType{}).valueMethod, "function.myType.valueMethod"},
35 | {(&myType{}).ValueMethod, "function.myType.ValueMethod"},
36 | {(*myType).pointerMethod, "function.myType.pointerMethod"},
37 | {(*myType).PointerMethod, "function.myType.PointerMethod"},
38 | {(&myType{}).pointerMethod, "function.myType.pointerMethod"},
39 | {(&myType{}).PointerMethod, "function.myType.PointerMethod"},
40 | {(*myType).Write, "function.myType.Write"},
41 | {(&myType{}).Write, "bytes.Buffer.Write"},
42 | }
43 | for _, tt := range tests {
44 | t.Run("", func(t *testing.T) {
45 | got := NameOf(reflect.ValueOf(tt.fnc))
46 | if got != tt.want {
47 | t.Errorf("NameOf() = %v, want %v", got, tt.want)
48 | }
49 | })
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/testprotos/protos.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package testprotos
6 |
7 | func Equal(x, y Message) bool {
8 | if x == nil || y == nil {
9 | return x == nil && y == nil
10 | }
11 | return x.String() == y.String()
12 | }
13 |
14 | type Message interface {
15 | Proto()
16 | String() string
17 | }
18 |
19 | type proto interface {
20 | Proto()
21 | }
22 |
23 | type notComparable struct {
24 | unexportedField func()
25 | }
26 |
27 | type Stringer struct{ X string }
28 |
29 | func (s *Stringer) String() string { return s.X }
30 |
31 | // Project1 protocol buffers
32 | type (
33 | Eagle_States int
34 | Eagle_MissingCalls int
35 | Dreamer_States int
36 | Dreamer_MissingCalls int
37 | Slap_States int
38 | Goat_States int
39 | Donkey_States int
40 | SummerType int
41 |
42 | Eagle struct {
43 | proto
44 | notComparable
45 | Stringer
46 | }
47 | Dreamer struct {
48 | proto
49 | notComparable
50 | Stringer
51 | }
52 | Slap struct {
53 | proto
54 | notComparable
55 | Stringer
56 | }
57 | Goat struct {
58 | proto
59 | notComparable
60 | Stringer
61 | }
62 | Donkey struct {
63 | proto
64 | notComparable
65 | Stringer
66 | }
67 | )
68 |
69 | // Project2 protocol buffers
70 | type (
71 | Germ struct {
72 | proto
73 | notComparable
74 | Stringer
75 | }
76 | Dish struct {
77 | proto
78 | notComparable
79 | Stringer
80 | }
81 | )
82 |
83 | // Project3 protocol buffers
84 | type (
85 | Dirt struct {
86 | proto
87 | notComparable
88 | Stringer
89 | }
90 | Wizard struct {
91 | proto
92 | notComparable
93 | Stringer
94 | }
95 | Sadistic struct {
96 | proto
97 | notComparable
98 | Stringer
99 | }
100 | )
101 |
102 | // Project4 protocol buffers
103 | type (
104 | HoneyStatus int
105 | PoisonType int
106 | MetaData struct {
107 | proto
108 | notComparable
109 | Stringer
110 | }
111 | Restrictions struct {
112 | proto
113 | notComparable
114 | Stringer
115 | }
116 | )
117 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project1.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package teststructs
6 |
7 | import (
8 | "time"
9 |
10 | pb "github.com/google/go-cmp/cmp/internal/testprotos"
11 | )
12 |
13 | // This is an sanitized example of equality from a real use-case.
14 | // The original equality function was as follows:
15 | /*
16 | func equalEagle(x, y Eagle) bool {
17 | if x.Name != y.Name &&
18 | !reflect.DeepEqual(x.Hounds, y.Hounds) &&
19 | x.Desc != y.Desc &&
20 | x.DescLong != y.DescLong &&
21 | x.Prong != y.Prong &&
22 | x.StateGoverner != y.StateGoverner &&
23 | x.PrankRating != y.PrankRating &&
24 | x.FunnyPrank != y.FunnyPrank &&
25 | !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) {
26 | return false
27 | }
28 |
29 | if len(x.Dreamers) != len(y.Dreamers) {
30 | return false
31 | }
32 | for i := range x.Dreamers {
33 | if !equalDreamer(x.Dreamers[i], y.Dreamers[i]) {
34 | return false
35 | }
36 | }
37 | if len(x.Slaps) != len(y.Slaps) {
38 | return false
39 | }
40 | for i := range x.Slaps {
41 | if !equalSlap(x.Slaps[i], y.Slaps[i]) {
42 | return false
43 | }
44 | }
45 | return true
46 | }
47 | func equalDreamer(x, y Dreamer) bool {
48 | if x.Name != y.Name ||
49 | x.Desc != y.Desc ||
50 | x.DescLong != y.DescLong ||
51 | x.ContSlapsInterval != y.ContSlapsInterval ||
52 | x.Ornamental != y.Ornamental ||
53 | x.Amoeba != y.Amoeba ||
54 | x.Heroes != y.Heroes ||
55 | x.FloppyDisk != y.FloppyDisk ||
56 | x.MightiestDuck != y.MightiestDuck ||
57 | x.FunnyPrank != y.FunnyPrank ||
58 | !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) {
59 |
60 | return false
61 | }
62 | if len(x.Animal) != len(y.Animal) {
63 | return false
64 | }
65 | for i := range x.Animal {
66 | vx := x.Animal[i]
67 | vy := y.Animal[i]
68 | if reflect.TypeOf(x.Animal) != reflect.TypeOf(y.Animal) {
69 | return false
70 | }
71 | switch vx.(type) {
72 | case Goat:
73 | if !equalGoat(vx.(Goat), vy.(Goat)) {
74 | return false
75 | }
76 | case Donkey:
77 | if !equalDonkey(vx.(Donkey), vy.(Donkey)) {
78 | return false
79 | }
80 | default:
81 | panic(fmt.Sprintf("unknown type: %T", vx))
82 | }
83 | }
84 | if len(x.PreSlaps) != len(y.PreSlaps) {
85 | return false
86 | }
87 | for i := range x.PreSlaps {
88 | if !equalSlap(x.PreSlaps[i], y.PreSlaps[i]) {
89 | return false
90 | }
91 | }
92 | if len(x.ContSlaps) != len(y.ContSlaps) {
93 | return false
94 | }
95 | for i := range x.ContSlaps {
96 | if !equalSlap(x.ContSlaps[i], y.ContSlaps[i]) {
97 | return false
98 | }
99 | }
100 | return true
101 | }
102 | func equalSlap(x, y Slap) bool {
103 | return x.Name == y.Name &&
104 | x.Desc == y.Desc &&
105 | x.DescLong == y.DescLong &&
106 | pb.Equal(x.Args, y.Args) &&
107 | x.Tense == y.Tense &&
108 | x.Interval == y.Interval &&
109 | x.Homeland == y.Homeland &&
110 | x.FunnyPrank == y.FunnyPrank &&
111 | pb.Equal(x.Immutable.Proto(), y.Immutable.Proto())
112 | }
113 | func equalGoat(x, y Goat) bool {
114 | if x.Target != y.Target ||
115 | x.FunnyPrank != y.FunnyPrank ||
116 | !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) {
117 | return false
118 | }
119 | if len(x.Slaps) != len(y.Slaps) {
120 | return false
121 | }
122 | for i := range x.Slaps {
123 | if !equalSlap(x.Slaps[i], y.Slaps[i]) {
124 | return false
125 | }
126 | }
127 | return true
128 | }
129 | func equalDonkey(x, y Donkey) bool {
130 | return x.Pause == y.Pause &&
131 | x.Sleep == y.Sleep &&
132 | x.FunnyPrank == y.FunnyPrank &&
133 | pb.Equal(x.Immutable.Proto(), y.Immutable.Proto())
134 | }
135 | */
136 |
137 | type Eagle struct {
138 | Name string
139 | Hounds []string
140 | Desc string
141 | DescLong string
142 | Dreamers []Dreamer
143 | Prong int64
144 | Slaps []Slap
145 | StateGoverner string
146 | PrankRating string
147 | FunnyPrank string
148 | Immutable *EagleImmutable
149 | }
150 |
151 | type EagleImmutable struct {
152 | ID string
153 | State *pb.Eagle_States
154 | MissingCall *pb.Eagle_MissingCalls
155 | Birthday time.Time
156 | Death time.Time
157 | Started time.Time
158 | LastUpdate time.Time
159 | Creator string
160 | empty bool
161 | }
162 |
163 | type Dreamer struct {
164 | Name string
165 | Desc string
166 | DescLong string
167 | PreSlaps []Slap
168 | ContSlaps []Slap
169 | ContSlapsInterval int32
170 | Animal []interface{} // Could be either Goat or Donkey
171 | Ornamental bool
172 | Amoeba int64
173 | Heroes int32
174 | FloppyDisk int32
175 | MightiestDuck bool
176 | FunnyPrank string
177 | Immutable *DreamerImmutable
178 | }
179 |
180 | type DreamerImmutable struct {
181 | ID string
182 | State *pb.Dreamer_States
183 | MissingCall *pb.Dreamer_MissingCalls
184 | Calls int32
185 | Started time.Time
186 | Stopped time.Time
187 | LastUpdate time.Time
188 | empty bool
189 | }
190 |
191 | type Slap struct {
192 | Name string
193 | Desc string
194 | DescLong string
195 | Args pb.Message
196 | Tense int32
197 | Interval int32
198 | Homeland uint32
199 | FunnyPrank string
200 | Immutable *SlapImmutable
201 | }
202 |
203 | type SlapImmutable struct {
204 | ID string
205 | Out pb.Message
206 | MildSlap bool
207 | PrettyPrint string
208 | State *pb.Slap_States
209 | Started time.Time
210 | Stopped time.Time
211 | LastUpdate time.Time
212 | LoveRadius *LoveRadius
213 | empty bool
214 | }
215 |
216 | type Goat struct {
217 | Target string
218 | Slaps []Slap
219 | FunnyPrank string
220 | Immutable *GoatImmutable
221 | }
222 |
223 | type GoatImmutable struct {
224 | ID string
225 | State *pb.Goat_States
226 | Started time.Time
227 | Stopped time.Time
228 | LastUpdate time.Time
229 | empty bool
230 | }
231 | type Donkey struct {
232 | Pause bool
233 | Sleep int32
234 | FunnyPrank string
235 | Immutable *DonkeyImmutable
236 | }
237 |
238 | type DonkeyImmutable struct {
239 | ID string
240 | State *pb.Donkey_States
241 | Started time.Time
242 | Stopped time.Time
243 | LastUpdate time.Time
244 | empty bool
245 | }
246 |
247 | type LoveRadius struct {
248 | Summer *SummerLove
249 | empty bool
250 | }
251 |
252 | type SummerLove struct {
253 | Summary *SummerLoveSummary
254 | empty bool
255 | }
256 |
257 | type SummerLoveSummary struct {
258 | Devices []string
259 | ChangeType []pb.SummerType
260 | empty bool
261 | }
262 |
263 | func (EagleImmutable) Proto() *pb.Eagle { return nil }
264 | func (DreamerImmutable) Proto() *pb.Dreamer { return nil }
265 | func (SlapImmutable) Proto() *pb.Slap { return nil }
266 | func (GoatImmutable) Proto() *pb.Goat { return nil }
267 | func (DonkeyImmutable) Proto() *pb.Donkey { return nil }
268 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project2.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package teststructs
6 |
7 | import (
8 | "time"
9 |
10 | pb "github.com/google/go-cmp/cmp/internal/testprotos"
11 | )
12 |
13 | // This is an sanitized example of equality from a real use-case.
14 | // The original equality function was as follows:
15 | /*
16 | func equalBatch(b1, b2 *GermBatch) bool {
17 | for _, b := range []*GermBatch{b1, b2} {
18 | for _, l := range b.DirtyGerms {
19 | sort.Slice(l, func(i, j int) bool { return l[i].String() < l[j].String() })
20 | }
21 | for _, l := range b.CleanGerms {
22 | sort.Slice(l, func(i, j int) bool { return l[i].String() < l[j].String() })
23 | }
24 | }
25 | if !pb.DeepEqual(b1.DirtyGerms, b2.DirtyGerms) ||
26 | !pb.DeepEqual(b1.CleanGerms, b2.CleanGerms) ||
27 | !pb.DeepEqual(b1.GermMap, b2.GermMap) {
28 | return false
29 | }
30 | if len(b1.DishMap) != len(b2.DishMap) {
31 | return false
32 | }
33 | for id := range b1.DishMap {
34 | kpb1, err1 := b1.DishMap[id].Proto()
35 | kpb2, err2 := b2.DishMap[id].Proto()
36 | if !pb.Equal(kpb1, kpb2) || !reflect.DeepEqual(err1, err2) {
37 | return false
38 | }
39 | }
40 | return b1.HasPreviousResult == b2.HasPreviousResult &&
41 | b1.DirtyID == b2.DirtyID &&
42 | b1.CleanID == b2.CleanID &&
43 | b1.GermStrain == b2.GermStrain &&
44 | b1.TotalDirtyGerms == b2.TotalDirtyGerms &&
45 | b1.InfectedAt.Equal(b2.InfectedAt)
46 | }
47 | */
48 |
49 | type GermBatch struct {
50 | DirtyGerms, CleanGerms map[int32][]*pb.Germ
51 | GermMap map[int32]*pb.Germ
52 | DishMap map[int32]*Dish
53 | HasPreviousResult bool
54 | DirtyID, CleanID int32
55 | GermStrain int32
56 | TotalDirtyGerms int
57 | InfectedAt time.Time
58 | }
59 |
60 | type Dish struct {
61 | pb *pb.Dish
62 | err error
63 | }
64 |
65 | func CreateDish(m *pb.Dish, err error) *Dish {
66 | return &Dish{pb: m, err: err}
67 | }
68 |
69 | func (d *Dish) Proto() (*pb.Dish, error) {
70 | if d.err != nil {
71 | return nil, d.err
72 | }
73 | return d.pb, nil
74 | }
75 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project3.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package teststructs
6 |
7 | import (
8 | "sync"
9 |
10 | pb "github.com/google/go-cmp/cmp/internal/testprotos"
11 | )
12 |
13 | // This is an sanitized example of equality from a real use-case.
14 | // The original equality function was as follows:
15 | /*
16 | func equalDirt(x, y *Dirt) bool {
17 | if !reflect.DeepEqual(x.table, y.table) ||
18 | !reflect.DeepEqual(x.ts, y.ts) ||
19 | x.Discord != y.Discord ||
20 | !pb.Equal(&x.Proto, &y.Proto) ||
21 | len(x.wizard) != len(y.wizard) ||
22 | len(x.sadistic) != len(y.sadistic) ||
23 | x.lastTime != y.lastTime {
24 | return false
25 | }
26 | for k, vx := range x.wizard {
27 | vy, ok := y.wizard[k]
28 | if !ok || !pb.Equal(vx, vy) {
29 | return false
30 | }
31 | }
32 | for k, vx := range x.sadistic {
33 | vy, ok := y.sadistic[k]
34 | if !ok || !pb.Equal(vx, vy) {
35 | return false
36 | }
37 | }
38 | return true
39 | }
40 | */
41 |
42 | type FakeMutex struct {
43 | sync.Locker
44 | x struct{}
45 | }
46 |
47 | type Dirt struct {
48 | table Table // Always concrete type of MockTable
49 | ts Timestamp
50 | Discord DiscordState
51 | Proto pb.Dirt
52 | wizard map[string]*pb.Wizard
53 | sadistic map[string]*pb.Sadistic
54 | lastTime int64
55 | mu FakeMutex
56 | }
57 |
58 | type DiscordState int
59 |
60 | type Timestamp int64
61 |
62 | func (d *Dirt) SetTable(t Table) { d.table = t }
63 | func (d *Dirt) SetTimestamp(t Timestamp) { d.ts = t }
64 | func (d *Dirt) SetWizard(m map[string]*pb.Wizard) { d.wizard = m }
65 | func (d *Dirt) SetSadistic(m map[string]*pb.Sadistic) { d.sadistic = m }
66 | func (d *Dirt) SetLastTime(t int64) { d.lastTime = t }
67 |
68 | type Table interface {
69 | Operation1() error
70 | Operation2() error
71 | Operation3() error
72 | }
73 |
74 | type MockTable struct {
75 | state []string
76 | }
77 |
78 | func CreateMockTable(s []string) *MockTable { return &MockTable{s} }
79 | func (mt *MockTable) Operation1() error { return nil }
80 | func (mt *MockTable) Operation2() error { return nil }
81 | func (mt *MockTable) Operation3() error { return nil }
82 | func (mt *MockTable) State() []string { return mt.state }
83 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project4.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package teststructs
6 |
7 | import (
8 | "time"
9 |
10 | pb "github.com/google/go-cmp/cmp/internal/testprotos"
11 | )
12 |
13 | // This is an sanitized example of equality from a real use-case.
14 | // The original equality function was as follows:
15 | /*
16 | func equalCartel(x, y Cartel) bool {
17 | if !(equalHeadquarter(x.Headquarter, y.Headquarter) &&
18 | x.Source() == y.Source() &&
19 | x.CreationDate().Equal(y.CreationDate()) &&
20 | x.Boss() == y.Boss() &&
21 | x.LastCrimeDate().Equal(y.LastCrimeDate())) {
22 | return false
23 | }
24 | if len(x.Poisons()) != len(y.Poisons()) {
25 | return false
26 | }
27 | for i := range x.Poisons() {
28 | if !equalPoison(*x.Poisons()[i], *y.Poisons()[i]) {
29 | return false
30 | }
31 | }
32 | return true
33 | }
34 | func equalHeadquarter(x, y Headquarter) bool {
35 | xr, yr := x.Restrictions(), y.Restrictions()
36 | return x.ID() == y.ID() &&
37 | x.Location() == y.Location() &&
38 | reflect.DeepEqual(x.SubDivisions(), y.SubDivisions()) &&
39 | x.IncorporatedDate().Equal(y.IncorporatedDate()) &&
40 | pb.Equal(x.MetaData(), y.MetaData()) &&
41 | bytes.Equal(x.PrivateMessage(), y.PrivateMessage()) &&
42 | bytes.Equal(x.PublicMessage(), y.PublicMessage()) &&
43 | x.HorseBack() == y.HorseBack() &&
44 | x.Rattle() == y.Rattle() &&
45 | x.Convulsion() == y.Convulsion() &&
46 | x.Expansion() == y.Expansion() &&
47 | x.Status() == y.Status() &&
48 | pb.Equal(&xr, &yr) &&
49 | x.CreationTime().Equal(y.CreationTime())
50 | }
51 | func equalPoison(x, y Poison) bool {
52 | return x.PoisonType() == y.PoisonType() &&
53 | x.Expiration().Equal(y.Expiration()) &&
54 | x.Manufacturer() == y.Manufacturer() &&
55 | x.Potency() == y.Potency()
56 | }
57 | */
58 |
59 | type Cartel struct {
60 | Headquarter
61 | source string
62 | creationDate time.Time
63 | boss string
64 | lastCrimeDate time.Time
65 | poisons []*Poison
66 | }
67 |
68 | func (p Cartel) Source() string { return p.source }
69 | func (p Cartel) CreationDate() time.Time { return p.creationDate }
70 | func (p Cartel) Boss() string { return p.boss }
71 | func (p Cartel) LastCrimeDate() time.Time { return p.lastCrimeDate }
72 | func (p Cartel) Poisons() []*Poison { return p.poisons }
73 |
74 | func (p *Cartel) SetSource(x string) { p.source = x }
75 | func (p *Cartel) SetCreationDate(x time.Time) { p.creationDate = x }
76 | func (p *Cartel) SetBoss(x string) { p.boss = x }
77 | func (p *Cartel) SetLastCrimeDate(x time.Time) { p.lastCrimeDate = x }
78 | func (p *Cartel) SetPoisons(x []*Poison) { p.poisons = x }
79 |
80 | type Headquarter struct {
81 | id uint64
82 | location string
83 | subDivisions []string
84 | incorporatedDate time.Time
85 | metaData *pb.MetaData
86 | privateMessage []byte
87 | publicMessage []byte
88 | horseBack string
89 | rattle string
90 | convulsion bool
91 | expansion uint64
92 | status pb.HoneyStatus
93 | restrictions pb.Restrictions
94 | creationTime time.Time
95 | }
96 |
97 | func (hq Headquarter) ID() uint64 { return hq.id }
98 | func (hq Headquarter) Location() string { return hq.location }
99 | func (hq Headquarter) SubDivisions() []string { return hq.subDivisions }
100 | func (hq Headquarter) IncorporatedDate() time.Time { return hq.incorporatedDate }
101 | func (hq Headquarter) MetaData() *pb.MetaData { return hq.metaData }
102 | func (hq Headquarter) PrivateMessage() []byte { return hq.privateMessage }
103 | func (hq Headquarter) PublicMessage() []byte { return hq.publicMessage }
104 | func (hq Headquarter) HorseBack() string { return hq.horseBack }
105 | func (hq Headquarter) Rattle() string { return hq.rattle }
106 | func (hq Headquarter) Convulsion() bool { return hq.convulsion }
107 | func (hq Headquarter) Expansion() uint64 { return hq.expansion }
108 | func (hq Headquarter) Status() pb.HoneyStatus { return hq.status }
109 | func (hq Headquarter) Restrictions() pb.Restrictions { return hq.restrictions }
110 | func (hq Headquarter) CreationTime() time.Time { return hq.creationTime }
111 |
112 | func (hq *Headquarter) SetID(x uint64) { hq.id = x }
113 | func (hq *Headquarter) SetLocation(x string) { hq.location = x }
114 | func (hq *Headquarter) SetSubDivisions(x []string) { hq.subDivisions = x }
115 | func (hq *Headquarter) SetIncorporatedDate(x time.Time) { hq.incorporatedDate = x }
116 | func (hq *Headquarter) SetMetaData(x *pb.MetaData) { hq.metaData = x }
117 | func (hq *Headquarter) SetPrivateMessage(x []byte) { hq.privateMessage = x }
118 | func (hq *Headquarter) SetPublicMessage(x []byte) { hq.publicMessage = x }
119 | func (hq *Headquarter) SetHorseBack(x string) { hq.horseBack = x }
120 | func (hq *Headquarter) SetRattle(x string) { hq.rattle = x }
121 | func (hq *Headquarter) SetConvulsion(x bool) { hq.convulsion = x }
122 | func (hq *Headquarter) SetExpansion(x uint64) { hq.expansion = x }
123 | func (hq *Headquarter) SetStatus(x pb.HoneyStatus) { hq.status = x }
124 | func (hq *Headquarter) SetRestrictions(x pb.Restrictions) { hq.restrictions = x }
125 | func (hq *Headquarter) SetCreationTime(x time.Time) { hq.creationTime = x }
126 |
127 | type Poison struct {
128 | poisonType pb.PoisonType
129 | expiration time.Time
130 | manufacturer string
131 | potency int
132 | }
133 |
134 | func (p Poison) PoisonType() pb.PoisonType { return p.poisonType }
135 | func (p Poison) Expiration() time.Time { return p.expiration }
136 | func (p Poison) Manufacturer() string { return p.manufacturer }
137 | func (p Poison) Potency() int { return p.potency }
138 |
139 | func (p *Poison) SetPoisonType(x pb.PoisonType) { p.poisonType = x }
140 | func (p *Poison) SetExpiration(x time.Time) { p.expiration = x }
141 | func (p *Poison) SetManufacturer(x string) { p.manufacturer = x }
142 | func (p *Poison) SetPotency(x int) { p.potency = x }
143 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/teststructs/structs.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package teststructs
6 |
7 | type InterfaceA interface {
8 | InterfaceA()
9 | }
10 |
11 | type (
12 | StructA struct{ X string } // Equal method on value receiver
13 | StructB struct{ X string } // Equal method on pointer receiver
14 | StructC struct{ X string } // Equal method (with interface argument) on value receiver
15 | StructD struct{ X string } // Equal method (with interface argument) on pointer receiver
16 | StructE struct{ X string } // Equal method (with interface argument on value receiver) on pointer receiver
17 | StructF struct{ X string } // Equal method (with interface argument on pointer receiver) on value receiver
18 |
19 | // These embed the above types as a value.
20 | StructA1 struct {
21 | StructA
22 | X string
23 | }
24 | StructB1 struct {
25 | StructB
26 | X string
27 | }
28 | StructC1 struct {
29 | StructC
30 | X string
31 | }
32 | StructD1 struct {
33 | StructD
34 | X string
35 | }
36 | StructE1 struct {
37 | StructE
38 | X string
39 | }
40 | StructF1 struct {
41 | StructF
42 | X string
43 | }
44 |
45 | // These embed the above types as a pointer.
46 | StructA2 struct {
47 | *StructA
48 | X string
49 | }
50 | StructB2 struct {
51 | *StructB
52 | X string
53 | }
54 | StructC2 struct {
55 | *StructC
56 | X string
57 | }
58 | StructD2 struct {
59 | *StructD
60 | X string
61 | }
62 | StructE2 struct {
63 | *StructE
64 | X string
65 | }
66 | StructF2 struct {
67 | *StructF
68 | X string
69 | }
70 |
71 | StructNo struct{ X string } // Equal method (with interface argument) on non-satisfying receiver
72 |
73 | AssignA func() int
74 | AssignB struct{ A int }
75 | AssignC chan bool
76 | AssignD <-chan bool
77 | )
78 |
79 | func (x StructA) Equal(y StructA) bool { return true }
80 | func (x *StructB) Equal(y *StructB) bool { return true }
81 | func (x StructC) Equal(y InterfaceA) bool { return true }
82 | func (x StructC) InterfaceA() {}
83 | func (x *StructD) Equal(y InterfaceA) bool { return true }
84 | func (x *StructD) InterfaceA() {}
85 | func (x *StructE) Equal(y InterfaceA) bool { return true }
86 | func (x StructE) InterfaceA() {}
87 | func (x StructF) Equal(y InterfaceA) bool { return true }
88 | func (x *StructF) InterfaceA() {}
89 | func (x StructNo) Equal(y InterfaceA) bool { return true }
90 |
91 | func (x AssignA) Equal(y func() int) bool { return true }
92 | func (x AssignB) Equal(y struct{ A int }) bool { return true }
93 | func (x AssignC) Equal(y chan bool) bool { return true }
94 | func (x AssignD) Equal(y <-chan bool) bool { return true }
95 |
96 | var _ = func(
97 | a StructA, b StructB, c StructC, d StructD, e StructE, f StructF,
98 | ap *StructA, bp *StructB, cp *StructC, dp *StructD, ep *StructE, fp *StructF,
99 | a1 StructA1, b1 StructB1, c1 StructC1, d1 StructD1, e1 StructE1, f1 StructF1,
100 | a2 StructA2, b2 StructB2, c2 StructC2, d2 StructD2, e2 StructE2, f2 StructF1,
101 | ) {
102 | a.Equal(a)
103 | b.Equal(&b)
104 | c.Equal(c)
105 | d.Equal(&d)
106 | e.Equal(e)
107 | f.Equal(&f)
108 |
109 | ap.Equal(*ap)
110 | bp.Equal(bp)
111 | cp.Equal(*cp)
112 | dp.Equal(dp)
113 | ep.Equal(*ep)
114 | fp.Equal(fp)
115 |
116 | a1.Equal(a1.StructA)
117 | b1.Equal(&b1.StructB)
118 | c1.Equal(c1)
119 | d1.Equal(&d1)
120 | e1.Equal(e1)
121 | f1.Equal(&f1)
122 |
123 | a2.Equal(*a2.StructA)
124 | b2.Equal(b2.StructB)
125 | c2.Equal(c2)
126 | d2.Equal(&d2)
127 | e2.Equal(e2)
128 | f2.Equal(&f2)
129 | }
130 |
131 | type (
132 | privateStruct struct{ Public, private int }
133 | PublicStruct struct{ Public, private int }
134 | ParentStructA struct{ privateStruct }
135 | ParentStructB struct{ PublicStruct }
136 | ParentStructC struct {
137 | privateStruct
138 | Public, private int
139 | }
140 | ParentStructD struct {
141 | PublicStruct
142 | Public, private int
143 | }
144 | ParentStructE struct {
145 | privateStruct
146 | PublicStruct
147 | }
148 | ParentStructF struct {
149 | privateStruct
150 | PublicStruct
151 | Public, private int
152 | }
153 | ParentStructG struct {
154 | *privateStruct
155 | }
156 | ParentStructH struct {
157 | *PublicStruct
158 | }
159 | ParentStructI struct {
160 | *privateStruct
161 | *PublicStruct
162 | }
163 | ParentStructJ struct {
164 | *privateStruct
165 | *PublicStruct
166 | Public PublicStruct
167 | private privateStruct
168 | }
169 | )
170 |
171 | func NewParentStructG() *ParentStructG {
172 | return &ParentStructG{new(privateStruct)}
173 | }
174 | func NewParentStructH() *ParentStructH {
175 | return &ParentStructH{new(PublicStruct)}
176 | }
177 | func NewParentStructI() *ParentStructI {
178 | return &ParentStructI{new(privateStruct), new(PublicStruct)}
179 | }
180 | func NewParentStructJ() *ParentStructJ {
181 | return &ParentStructJ{
182 | privateStruct: new(privateStruct), PublicStruct: new(PublicStruct),
183 | }
184 | }
185 | func (s *privateStruct) SetPrivate(i int) { s.private = i }
186 | func (s *PublicStruct) SetPrivate(i int) { s.private = i }
187 | func (s *ParentStructC) SetPrivate(i int) { s.private = i }
188 | func (s *ParentStructD) SetPrivate(i int) { s.private = i }
189 | func (s *ParentStructF) SetPrivate(i int) { s.private = i }
190 | func (s *ParentStructA) PrivateStruct() *privateStruct { return &s.privateStruct }
191 | func (s *ParentStructC) PrivateStruct() *privateStruct { return &s.privateStruct }
192 | func (s *ParentStructE) PrivateStruct() *privateStruct { return &s.privateStruct }
193 | func (s *ParentStructF) PrivateStruct() *privateStruct { return &s.privateStruct }
194 | func (s *ParentStructG) PrivateStruct() *privateStruct { return s.privateStruct }
195 | func (s *ParentStructI) PrivateStruct() *privateStruct { return s.privateStruct }
196 | func (s *ParentStructJ) PrivateStruct() *privateStruct { return s.privateStruct }
197 | func (s *ParentStructJ) Private() *privateStruct { return &s.private }
198 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // +build purego
6 |
7 | package value
8 |
9 | import "reflect"
10 |
11 | // Pointer is an opaque typed pointer and is guaranteed to be comparable.
12 | type Pointer struct {
13 | p uintptr
14 | t reflect.Type
15 | }
16 |
17 | // PointerOf returns a Pointer from v, which must be a
18 | // reflect.Ptr, reflect.Slice, or reflect.Map.
19 | func PointerOf(v reflect.Value) Pointer {
20 | // NOTE: Storing a pointer as an uintptr is technically incorrect as it
21 | // assumes that the GC implementation does not use a moving collector.
22 | return Pointer{v.Pointer(), v.Type()}
23 | }
24 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | // +build !purego
6 |
7 | package value
8 |
9 | import (
10 | "reflect"
11 | "unsafe"
12 | )
13 |
14 | // Pointer is an opaque typed pointer and is guaranteed to be comparable.
15 | type Pointer struct {
16 | p unsafe.Pointer
17 | t reflect.Type
18 | }
19 |
20 | // PointerOf returns a Pointer from v, which must be a
21 | // reflect.Ptr, reflect.Slice, or reflect.Map.
22 | func PointerOf(v reflect.Value) Pointer {
23 | // The proper representation of a pointer is unsafe.Pointer,
24 | // which is necessary if the GC ever uses a moving collector.
25 | return Pointer{unsafe.Pointer(v.Pointer()), v.Type()}
26 | }
27 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package value
6 |
7 | import (
8 | "fmt"
9 | "math"
10 | "reflect"
11 | "sort"
12 | )
13 |
14 | // SortKeys sorts a list of map keys, deduplicating keys if necessary.
15 | // The type of each value must be comparable.
16 | func SortKeys(vs []reflect.Value) []reflect.Value {
17 | if len(vs) == 0 {
18 | return vs
19 | }
20 |
21 | // Sort the map keys.
22 | sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) })
23 |
24 | // Deduplicate keys (fails for NaNs).
25 | vs2 := vs[:1]
26 | for _, v := range vs[1:] {
27 | if isLess(vs2[len(vs2)-1], v) {
28 | vs2 = append(vs2, v)
29 | }
30 | }
31 | return vs2
32 | }
33 |
34 | // isLess is a generic function for sorting arbitrary map keys.
35 | // The inputs must be of the same type and must be comparable.
36 | func isLess(x, y reflect.Value) bool {
37 | switch x.Type().Kind() {
38 | case reflect.Bool:
39 | return !x.Bool() && y.Bool()
40 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
41 | return x.Int() < y.Int()
42 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
43 | return x.Uint() < y.Uint()
44 | case reflect.Float32, reflect.Float64:
45 | // NOTE: This does not sort -0 as less than +0
46 | // since Go maps treat -0 and +0 as equal keys.
47 | fx, fy := x.Float(), y.Float()
48 | return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy)
49 | case reflect.Complex64, reflect.Complex128:
50 | cx, cy := x.Complex(), y.Complex()
51 | rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy)
52 | if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) {
53 | return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy)
54 | }
55 | return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry)
56 | case reflect.Ptr, reflect.UnsafePointer, reflect.Chan:
57 | return x.Pointer() < y.Pointer()
58 | case reflect.String:
59 | return x.String() < y.String()
60 | case reflect.Array:
61 | for i := 0; i < x.Len(); i++ {
62 | if isLess(x.Index(i), y.Index(i)) {
63 | return true
64 | }
65 | if isLess(y.Index(i), x.Index(i)) {
66 | return false
67 | }
68 | }
69 | return false
70 | case reflect.Struct:
71 | for i := 0; i < x.NumField(); i++ {
72 | if isLess(x.Field(i), y.Field(i)) {
73 | return true
74 | }
75 | if isLess(y.Field(i), x.Field(i)) {
76 | return false
77 | }
78 | }
79 | return false
80 | case reflect.Interface:
81 | vx, vy := x.Elem(), y.Elem()
82 | if !vx.IsValid() || !vy.IsValid() {
83 | return !vx.IsValid() && vy.IsValid()
84 | }
85 | tx, ty := vx.Type(), vy.Type()
86 | if tx == ty {
87 | return isLess(x.Elem(), y.Elem())
88 | }
89 | if tx.Kind() != ty.Kind() {
90 | return vx.Kind() < vy.Kind()
91 | }
92 | if tx.String() != ty.String() {
93 | return tx.String() < ty.String()
94 | }
95 | if tx.PkgPath() != ty.PkgPath() {
96 | return tx.PkgPath() < ty.PkgPath()
97 | }
98 | // This can happen in rare situations, so we fallback to just comparing
99 | // the unique pointer for a reflect.Type. This guarantees deterministic
100 | // ordering within a program, but it is obviously not stable.
101 | return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer()
102 | default:
103 | // Must be Func, Map, or Slice; which are not comparable.
104 | panic(fmt.Sprintf("%T is not comparable", x.Type()))
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/value/sort_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package value_test
6 |
7 | import (
8 | "math"
9 | "reflect"
10 | "testing"
11 |
12 | "github.com/google/go-cmp/cmp"
13 | "github.com/google/go-cmp/cmp/internal/value"
14 | )
15 |
16 | func TestSortKeys(t *testing.T) {
17 | type (
18 | MyString string
19 | MyArray [2]int
20 | MyStruct struct {
21 | A MyString
22 | B MyArray
23 | C chan float64
24 | }
25 | EmptyStruct struct{}
26 | )
27 |
28 | opts := []cmp.Option{
29 | cmp.Comparer(func(x, y float64) bool {
30 | if math.IsNaN(x) && math.IsNaN(y) {
31 | return true
32 | }
33 | return x == y
34 | }),
35 | cmp.Comparer(func(x, y complex128) bool {
36 | rx, ix, ry, iy := real(x), imag(x), real(y), imag(y)
37 | if math.IsNaN(rx) && math.IsNaN(ry) {
38 | rx, ry = 0, 0
39 | }
40 | if math.IsNaN(ix) && math.IsNaN(iy) {
41 | ix, iy = 0, 0
42 | }
43 | return rx == ry && ix == iy
44 | }),
45 | cmp.Comparer(func(x, y chan bool) bool { return true }),
46 | cmp.Comparer(func(x, y chan int) bool { return true }),
47 | cmp.Comparer(func(x, y chan float64) bool { return true }),
48 | cmp.Comparer(func(x, y chan interface{}) bool { return true }),
49 | cmp.Comparer(func(x, y *int) bool { return true }),
50 | }
51 |
52 | tests := []struct {
53 | in map[interface{}]bool // Set of keys to sort
54 | want []interface{}
55 | }{{
56 | in: map[interface{}]bool{1: true, 2: true, 3: true},
57 | want: []interface{}{1, 2, 3},
58 | }, {
59 | in: map[interface{}]bool{
60 | nil: true,
61 | true: true,
62 | false: true,
63 | -5: true,
64 | -55: true,
65 | -555: true,
66 | uint(1): true,
67 | uint(11): true,
68 | uint(111): true,
69 | "abc": true,
70 | "abcd": true,
71 | "abcde": true,
72 | "foo": true,
73 | "bar": true,
74 | MyString("abc"): true,
75 | MyString("abcd"): true,
76 | MyString("abcde"): true,
77 | new(int): true,
78 | new(int): true,
79 | make(chan bool): true,
80 | make(chan bool): true,
81 | make(chan int): true,
82 | make(chan interface{}): true,
83 | math.Inf(+1): true,
84 | math.Inf(-1): true,
85 | 1.2345: true,
86 | 12.345: true,
87 | 123.45: true,
88 | 1234.5: true,
89 | 0 + 0i: true,
90 | 1 + 0i: true,
91 | 2 + 0i: true,
92 | 0 + 1i: true,
93 | 0 + 2i: true,
94 | 0 + 3i: true,
95 | [2]int{2, 3}: true,
96 | [2]int{4, 0}: true,
97 | [2]int{2, 4}: true,
98 | MyArray([2]int{2, 4}): true,
99 | EmptyStruct{}: true,
100 | MyStruct{
101 | "bravo", [2]int{2, 3}, make(chan float64),
102 | }: true,
103 | MyStruct{
104 | "alpha", [2]int{3, 3}, make(chan float64),
105 | }: true,
106 | },
107 | want: []interface{}{
108 | nil, false, true,
109 | -555, -55, -5, uint(1), uint(11), uint(111),
110 | math.Inf(-1), 1.2345, 12.345, 123.45, 1234.5, math.Inf(+1),
111 | (0 + 0i), (0 + 1i), (0 + 2i), (0 + 3i), (1 + 0i), (2 + 0i),
112 | [2]int{2, 3}, [2]int{2, 4}, [2]int{4, 0}, MyArray([2]int{2, 4}),
113 | make(chan bool), make(chan bool), make(chan int), make(chan interface{}),
114 | new(int), new(int),
115 | "abc", "abcd", "abcde", "bar", "foo",
116 | MyString("abc"), MyString("abcd"), MyString("abcde"),
117 | EmptyStruct{},
118 | MyStruct{"alpha", [2]int{3, 3}, make(chan float64)},
119 | MyStruct{"bravo", [2]int{2, 3}, make(chan float64)},
120 | },
121 | }, {
122 | // NaN values cannot be properly deduplicated.
123 | // This is okay since map entries with NaN in the keys cannot be
124 | // retrieved anyways.
125 | in: map[interface{}]bool{
126 | math.NaN(): true,
127 | math.NaN(): true,
128 | complex(0, math.NaN()): true,
129 | complex(0, math.NaN()): true,
130 | complex(math.NaN(), 0): true,
131 | complex(math.NaN(), 0): true,
132 | complex(math.NaN(), math.NaN()): true,
133 | },
134 | want: []interface{}{
135 | math.NaN(),
136 | complex(math.NaN(), math.NaN()),
137 | complex(math.NaN(), 0),
138 | complex(0, math.NaN()),
139 | },
140 | }}
141 |
142 | for i, tt := range tests {
143 | // Intentionally pass the map via an unexported field to detect panics.
144 | // Unfortunately, we cannot actually test the keys without using unsafe.
145 | v := reflect.ValueOf(struct{ x map[interface{}]bool }{tt.in}).Field(0)
146 | value.SortKeys(append(v.MapKeys(), v.MapKeys()...))
147 |
148 | // Try again, with keys that have read-write access in reflect.
149 | v = reflect.ValueOf(tt.in)
150 | keys := append(v.MapKeys(), v.MapKeys()...)
151 | var got []interface{}
152 | for _, k := range value.SortKeys(keys) {
153 | got = append(got, k.Interface())
154 | }
155 | if d := cmp.Diff(got, tt.want, opts...); d != "" {
156 | t.Errorf("test %d, Sort() mismatch (-got +want):\n%s", i, d)
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package value
6 |
7 | import (
8 | "math"
9 | "reflect"
10 | )
11 |
12 | // IsZero reports whether v is the zero value.
13 | // This does not rely on Interface and so can be used on unexported fields.
14 | func IsZero(v reflect.Value) bool {
15 | switch v.Kind() {
16 | case reflect.Bool:
17 | return v.Bool() == false
18 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
19 | return v.Int() == 0
20 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
21 | return v.Uint() == 0
22 | case reflect.Float32, reflect.Float64:
23 | return math.Float64bits(v.Float()) == 0
24 | case reflect.Complex64, reflect.Complex128:
25 | return math.Float64bits(real(v.Complex())) == 0 && math.Float64bits(imag(v.Complex())) == 0
26 | case reflect.String:
27 | return v.String() == ""
28 | case reflect.UnsafePointer:
29 | return v.Pointer() == 0
30 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
31 | return v.IsNil()
32 | case reflect.Array:
33 | for i := 0; i < v.Len(); i++ {
34 | if !IsZero(v.Index(i)) {
35 | return false
36 | }
37 | }
38 | return true
39 | case reflect.Struct:
40 | for i := 0; i < v.NumField(); i++ {
41 | if !IsZero(v.Field(i)) {
42 | return false
43 | }
44 | }
45 | return true
46 | }
47 | return false
48 | }
49 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/internal/value/zero_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package value
6 |
7 | import (
8 | "archive/tar"
9 | "math"
10 | "reflect"
11 | "testing"
12 | )
13 |
14 | func TestIsZero(t *testing.T) {
15 | tests := []struct {
16 | in interface{}
17 | want bool
18 | }{
19 | {0, true},
20 | {1, false},
21 | {"", true},
22 | {"foo", false},
23 | {[]byte(nil), true},
24 | {[]byte{}, false},
25 | {map[string]bool(nil), true},
26 | {map[string]bool{}, false},
27 | {tar.Header{}, true},
28 | {&tar.Header{}, false},
29 | {tar.Header{Name: "foo"}, false},
30 | {(chan bool)(nil), true},
31 | {make(chan bool), false},
32 | {(func(*testing.T))(nil), true},
33 | {TestIsZero, false},
34 | {[...]int{0, 0, 0}, true},
35 | {[...]int{0, 1, 0}, false},
36 | {math.Copysign(0, +1), true},
37 | {math.Copysign(0, -1), false},
38 | {complex(math.Copysign(0, +1), math.Copysign(0, +1)), true},
39 | {complex(math.Copysign(0, -1), math.Copysign(0, +1)), false},
40 | {complex(math.Copysign(0, +1), math.Copysign(0, -1)), false},
41 | {complex(math.Copysign(0, -1), math.Copysign(0, -1)), false},
42 | }
43 |
44 | for _, tt := range tests {
45 | t.Run("", func(t *testing.T) {
46 | got := IsZero(reflect.ValueOf(tt.in))
47 | if got != tt.want {
48 | t.Errorf("IsZero(%v) = %v, want %v", tt.in, got, tt.want)
49 | }
50 | })
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/options_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp
6 |
7 | import (
8 | "io"
9 | "reflect"
10 | "strings"
11 | "testing"
12 |
13 | ts "github.com/google/go-cmp/cmp/internal/teststructs"
14 | )
15 |
16 | // Test that the creation of Option values with non-sensible inputs produces
17 | // a run-time panic with a decent error message
18 | func TestOptionPanic(t *testing.T) {
19 | type myBool bool
20 | tests := []struct {
21 | label string // Test description
22 | fnc interface{} // Option function to call
23 | args []interface{} // Arguments to pass in
24 | wantPanic string // Expected panic message
25 | }{{
26 | label: "AllowUnexported",
27 | fnc: AllowUnexported,
28 | args: []interface{}{},
29 | }, {
30 | label: "AllowUnexported",
31 | fnc: AllowUnexported,
32 | args: []interface{}{1},
33 | wantPanic: "invalid struct type",
34 | }, {
35 | label: "AllowUnexported",
36 | fnc: AllowUnexported,
37 | args: []interface{}{ts.StructA{}},
38 | }, {
39 | label: "AllowUnexported",
40 | fnc: AllowUnexported,
41 | args: []interface{}{ts.StructA{}, ts.StructB{}, ts.StructA{}},
42 | }, {
43 | label: "AllowUnexported",
44 | fnc: AllowUnexported,
45 | args: []interface{}{ts.StructA{}, &ts.StructB{}, ts.StructA{}},
46 | wantPanic: "invalid struct type",
47 | }, {
48 | label: "Comparer",
49 | fnc: Comparer,
50 | args: []interface{}{5},
51 | wantPanic: "invalid comparer function",
52 | }, {
53 | label: "Comparer",
54 | fnc: Comparer,
55 | args: []interface{}{func(x, y interface{}) bool { return true }},
56 | }, {
57 | label: "Comparer",
58 | fnc: Comparer,
59 | args: []interface{}{func(x, y io.Reader) bool { return true }},
60 | }, {
61 | label: "Comparer",
62 | fnc: Comparer,
63 | args: []interface{}{func(x, y io.Reader) myBool { return true }},
64 | wantPanic: "invalid comparer function",
65 | }, {
66 | label: "Comparer",
67 | fnc: Comparer,
68 | args: []interface{}{func(x string, y interface{}) bool { return true }},
69 | wantPanic: "invalid comparer function",
70 | }, {
71 | label: "Comparer",
72 | fnc: Comparer,
73 | args: []interface{}{(func(int, int) bool)(nil)},
74 | wantPanic: "invalid comparer function",
75 | }, {
76 | label: "Transformer",
77 | fnc: Transformer,
78 | args: []interface{}{"", 0},
79 | wantPanic: "invalid transformer function",
80 | }, {
81 | label: "Transformer",
82 | fnc: Transformer,
83 | args: []interface{}{"", func(int) int { return 0 }},
84 | }, {
85 | label: "Transformer",
86 | fnc: Transformer,
87 | args: []interface{}{"", func(bool) bool { return true }},
88 | }, {
89 | label: "Transformer",
90 | fnc: Transformer,
91 | args: []interface{}{"", func(int) bool { return true }},
92 | }, {
93 | label: "Transformer",
94 | fnc: Transformer,
95 | args: []interface{}{"", func(int, int) bool { return true }},
96 | wantPanic: "invalid transformer function",
97 | }, {
98 | label: "Transformer",
99 | fnc: Transformer,
100 | args: []interface{}{"", (func(int) uint)(nil)},
101 | wantPanic: "invalid transformer function",
102 | }, {
103 | label: "Transformer",
104 | fnc: Transformer,
105 | args: []interface{}{"Func", func(Path) Path { return nil }},
106 | }, {
107 | label: "Transformer",
108 | fnc: Transformer,
109 | args: []interface{}{"世界", func(int) bool { return true }},
110 | }, {
111 | label: "Transformer",
112 | fnc: Transformer,
113 | args: []interface{}{"/*", func(int) bool { return true }},
114 | wantPanic: "invalid name",
115 | }, {
116 | label: "Transformer",
117 | fnc: Transformer,
118 | args: []interface{}{"_", func(int) bool { return true }},
119 | }, {
120 | label: "FilterPath",
121 | fnc: FilterPath,
122 | args: []interface{}{(func(Path) bool)(nil), Ignore()},
123 | wantPanic: "invalid path filter function",
124 | }, {
125 | label: "FilterPath",
126 | fnc: FilterPath,
127 | args: []interface{}{func(Path) bool { return true }, Ignore()},
128 | }, {
129 | label: "FilterPath",
130 | fnc: FilterPath,
131 | args: []interface{}{func(Path) bool { return true }, Reporter(&defaultReporter{})},
132 | wantPanic: "invalid option type",
133 | }, {
134 | label: "FilterPath",
135 | fnc: FilterPath,
136 | args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Ignore()}},
137 | }, {
138 | label: "FilterPath",
139 | fnc: FilterPath,
140 | args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}},
141 | wantPanic: "invalid option type",
142 | }, {
143 | label: "FilterValues",
144 | fnc: FilterValues,
145 | args: []interface{}{0, Ignore()},
146 | wantPanic: "invalid values filter function",
147 | }, {
148 | label: "FilterValues",
149 | fnc: FilterValues,
150 | args: []interface{}{func(x, y int) bool { return true }, Ignore()},
151 | }, {
152 | label: "FilterValues",
153 | fnc: FilterValues,
154 | args: []interface{}{func(x, y interface{}) bool { return true }, Ignore()},
155 | }, {
156 | label: "FilterValues",
157 | fnc: FilterValues,
158 | args: []interface{}{func(x, y interface{}) myBool { return true }, Ignore()},
159 | wantPanic: "invalid values filter function",
160 | }, {
161 | label: "FilterValues",
162 | fnc: FilterValues,
163 | args: []interface{}{func(x io.Reader, y interface{}) bool { return true }, Ignore()},
164 | wantPanic: "invalid values filter function",
165 | }, {
166 | label: "FilterValues",
167 | fnc: FilterValues,
168 | args: []interface{}{(func(int, int) bool)(nil), Ignore()},
169 | wantPanic: "invalid values filter function",
170 | }, {
171 | label: "FilterValues",
172 | fnc: FilterValues,
173 | args: []interface{}{func(int, int) bool { return true }, Reporter(&defaultReporter{})},
174 | wantPanic: "invalid option type",
175 | }, {
176 | label: "FilterValues",
177 | fnc: FilterValues,
178 | args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Ignore()}},
179 | }, {
180 | label: "FilterValues",
181 | fnc: FilterValues,
182 | args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}},
183 | wantPanic: "invalid option type",
184 | }}
185 |
186 | for _, tt := range tests {
187 | t.Run(tt.label, func(t *testing.T) {
188 | var gotPanic string
189 | func() {
190 | defer func() {
191 | if ex := recover(); ex != nil {
192 | if s, ok := ex.(string); ok {
193 | gotPanic = s
194 | } else {
195 | panic(ex)
196 | }
197 | }
198 | }()
199 | var vargs []reflect.Value
200 | for _, arg := range tt.args {
201 | vargs = append(vargs, reflect.ValueOf(arg))
202 | }
203 | reflect.ValueOf(tt.fnc).Call(vargs)
204 | }()
205 | if tt.wantPanic == "" {
206 | if gotPanic != "" {
207 | t.Fatalf("unexpected panic message: %s", gotPanic)
208 | }
209 | } else {
210 | if !strings.Contains(gotPanic, tt.wantPanic) {
211 | t.Fatalf("panic message:\ngot: %s\nwant: %s", gotPanic, tt.wantPanic)
212 | }
213 | }
214 | })
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/path.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp
6 |
7 | import (
8 | "fmt"
9 | "reflect"
10 | "strings"
11 | "unicode"
12 | "unicode/utf8"
13 | )
14 |
15 | // Path is a list of PathSteps describing the sequence of operations to get
16 | // from some root type to the current position in the value tree.
17 | // The first Path element is always an operation-less PathStep that exists
18 | // simply to identify the initial type.
19 | //
20 | // When traversing structs with embedded structs, the embedded struct will
21 | // always be accessed as a field before traversing the fields of the
22 | // embedded struct themselves. That is, an exported field from the
23 | // embedded struct will never be accessed directly from the parent struct.
24 | type Path []PathStep
25 |
26 | // PathStep is a union-type for specific operations to traverse
27 | // a value's tree structure. Users of this package never need to implement
28 | // these types as values of this type will be returned by this package.
29 | //
30 | // Implementations of this interface are
31 | // StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform.
32 | type PathStep interface {
33 | String() string
34 |
35 | // Type is the resulting type after performing the path step.
36 | Type() reflect.Type
37 |
38 | // Values is the resulting values after performing the path step.
39 | // The type of each valid value is guaranteed to be identical to Type.
40 | //
41 | // In some cases, one or both may be invalid or have restrictions:
42 | // • For StructField, both are not interface-able if the current field
43 | // is unexported and the struct type is not explicitly permitted by
44 | // AllowUnexported to traverse unexported fields.
45 | // • For SliceIndex, one may be invalid if an element is missing from
46 | // either the x or y slice.
47 | // • For MapIndex, one may be invalid if an entry is missing from
48 | // either the x or y map.
49 | //
50 | // The provided values must not be mutated.
51 | Values() (vx, vy reflect.Value)
52 | }
53 |
54 | var (
55 | _ PathStep = StructField{}
56 | _ PathStep = SliceIndex{}
57 | _ PathStep = MapIndex{}
58 | _ PathStep = Indirect{}
59 | _ PathStep = TypeAssertion{}
60 | _ PathStep = Transform{}
61 | )
62 |
63 | func (pa *Path) push(s PathStep) {
64 | *pa = append(*pa, s)
65 | }
66 |
67 | func (pa *Path) pop() {
68 | *pa = (*pa)[:len(*pa)-1]
69 | }
70 |
71 | // Last returns the last PathStep in the Path.
72 | // If the path is empty, this returns a non-nil PathStep that reports a nil Type.
73 | func (pa Path) Last() PathStep {
74 | return pa.Index(-1)
75 | }
76 |
77 | // Index returns the ith step in the Path and supports negative indexing.
78 | // A negative index starts counting from the tail of the Path such that -1
79 | // refers to the last step, -2 refers to the second-to-last step, and so on.
80 | // If index is invalid, this returns a non-nil PathStep that reports a nil Type.
81 | func (pa Path) Index(i int) PathStep {
82 | if i < 0 {
83 | i = len(pa) + i
84 | }
85 | if i < 0 || i >= len(pa) {
86 | return pathStep{}
87 | }
88 | return pa[i]
89 | }
90 |
91 | // String returns the simplified path to a node.
92 | // The simplified path only contains struct field accesses.
93 | //
94 | // For example:
95 | // MyMap.MySlices.MyField
96 | func (pa Path) String() string {
97 | var ss []string
98 | for _, s := range pa {
99 | if _, ok := s.(StructField); ok {
100 | ss = append(ss, s.String())
101 | }
102 | }
103 | return strings.TrimPrefix(strings.Join(ss, ""), ".")
104 | }
105 |
106 | // GoString returns the path to a specific node using Go syntax.
107 | //
108 | // For example:
109 | // (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField
110 | func (pa Path) GoString() string {
111 | var ssPre, ssPost []string
112 | var numIndirect int
113 | for i, s := range pa {
114 | var nextStep PathStep
115 | if i+1 < len(pa) {
116 | nextStep = pa[i+1]
117 | }
118 | switch s := s.(type) {
119 | case Indirect:
120 | numIndirect++
121 | pPre, pPost := "(", ")"
122 | switch nextStep.(type) {
123 | case Indirect:
124 | continue // Next step is indirection, so let them batch up
125 | case StructField:
126 | numIndirect-- // Automatic indirection on struct fields
127 | case nil:
128 | pPre, pPost = "", "" // Last step; no need for parenthesis
129 | }
130 | if numIndirect > 0 {
131 | ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect))
132 | ssPost = append(ssPost, pPost)
133 | }
134 | numIndirect = 0
135 | continue
136 | case Transform:
137 | ssPre = append(ssPre, s.trans.name+"(")
138 | ssPost = append(ssPost, ")")
139 | continue
140 | }
141 | ssPost = append(ssPost, s.String())
142 | }
143 | for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 {
144 | ssPre[i], ssPre[j] = ssPre[j], ssPre[i]
145 | }
146 | return strings.Join(ssPre, "") + strings.Join(ssPost, "")
147 | }
148 |
149 | type pathStep struct {
150 | typ reflect.Type
151 | vx, vy reflect.Value
152 | }
153 |
154 | func (ps pathStep) Type() reflect.Type { return ps.typ }
155 | func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy }
156 | func (ps pathStep) String() string {
157 | if ps.typ == nil {
158 | return ""
159 | }
160 | s := ps.typ.String()
161 | if s == "" || strings.ContainsAny(s, "{}\n") {
162 | return "root" // Type too simple or complex to print
163 | }
164 | return fmt.Sprintf("{%s}", s)
165 | }
166 |
167 | // StructField represents a struct field access on a field called Name.
168 | type StructField struct{ *structField }
169 | type structField struct {
170 | pathStep
171 | name string
172 | idx int
173 |
174 | // These fields are used for forcibly accessing an unexported field.
175 | // pvx, pvy, and field are only valid if unexported is true.
176 | unexported bool
177 | mayForce bool // Forcibly allow visibility
178 | pvx, pvy reflect.Value // Parent values
179 | field reflect.StructField // Field information
180 | }
181 |
182 | func (sf StructField) Type() reflect.Type { return sf.typ }
183 | func (sf StructField) Values() (vx, vy reflect.Value) {
184 | if !sf.unexported {
185 | return sf.vx, sf.vy // CanInterface reports true
186 | }
187 |
188 | // Forcibly obtain read-write access to an unexported struct field.
189 | if sf.mayForce {
190 | vx = retrieveUnexportedField(sf.pvx, sf.field)
191 | vy = retrieveUnexportedField(sf.pvy, sf.field)
192 | return vx, vy // CanInterface reports true
193 | }
194 | return sf.vx, sf.vy // CanInterface reports false
195 | }
196 | func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) }
197 |
198 | // Name is the field name.
199 | func (sf StructField) Name() string { return sf.name }
200 |
201 | // Index is the index of the field in the parent struct type.
202 | // See reflect.Type.Field.
203 | func (sf StructField) Index() int { return sf.idx }
204 |
205 | // SliceIndex is an index operation on a slice or array at some index Key.
206 | type SliceIndex struct{ *sliceIndex }
207 | type sliceIndex struct {
208 | pathStep
209 | xkey, ykey int
210 | }
211 |
212 | func (si SliceIndex) Type() reflect.Type { return si.typ }
213 | func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy }
214 | func (si SliceIndex) String() string {
215 | switch {
216 | case si.xkey == si.ykey:
217 | return fmt.Sprintf("[%d]", si.xkey)
218 | case si.ykey == -1:
219 | // [5->?] means "I don't know where X[5] went"
220 | return fmt.Sprintf("[%d->?]", si.xkey)
221 | case si.xkey == -1:
222 | // [?->3] means "I don't know where Y[3] came from"
223 | return fmt.Sprintf("[?->%d]", si.ykey)
224 | default:
225 | // [5->3] means "X[5] moved to Y[3]"
226 | return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey)
227 | }
228 | }
229 |
230 | // Key is the index key; it may return -1 if in a split state
231 | func (si SliceIndex) Key() int {
232 | if si.xkey != si.ykey {
233 | return -1
234 | }
235 | return si.xkey
236 | }
237 |
238 | // SplitKeys are the indexes for indexing into slices in the
239 | // x and y values, respectively. These indexes may differ due to the
240 | // insertion or removal of an element in one of the slices, causing
241 | // all of the indexes to be shifted. If an index is -1, then that
242 | // indicates that the element does not exist in the associated slice.
243 | //
244 | // Key is guaranteed to return -1 if and only if the indexes returned
245 | // by SplitKeys are not the same. SplitKeys will never return -1 for
246 | // both indexes.
247 | func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey }
248 |
249 | // MapIndex is an index operation on a map at some index Key.
250 | type MapIndex struct{ *mapIndex }
251 | type mapIndex struct {
252 | pathStep
253 | key reflect.Value
254 | }
255 |
256 | func (mi MapIndex) Type() reflect.Type { return mi.typ }
257 | func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy }
258 | func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
259 |
260 | // Key is the value of the map key.
261 | func (mi MapIndex) Key() reflect.Value { return mi.key }
262 |
263 | // Indirect represents pointer indirection on the parent type.
264 | type Indirect struct{ *indirect }
265 | type indirect struct {
266 | pathStep
267 | }
268 |
269 | func (in Indirect) Type() reflect.Type { return in.typ }
270 | func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy }
271 | func (in Indirect) String() string { return "*" }
272 |
273 | // TypeAssertion represents a type assertion on an interface.
274 | type TypeAssertion struct{ *typeAssertion }
275 | type typeAssertion struct {
276 | pathStep
277 | }
278 |
279 | func (ta TypeAssertion) Type() reflect.Type { return ta.typ }
280 | func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy }
281 | func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) }
282 |
283 | // Transform is a transformation from the parent type to the current type.
284 | type Transform struct{ *transform }
285 | type transform struct {
286 | pathStep
287 | trans *transformer
288 | }
289 |
290 | func (tf Transform) Type() reflect.Type { return tf.typ }
291 | func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy }
292 | func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
293 |
294 | // Name is the name of the Transformer.
295 | func (tf Transform) Name() string { return tf.trans.name }
296 |
297 | // Func is the function pointer to the transformer function.
298 | func (tf Transform) Func() reflect.Value { return tf.trans.fnc }
299 |
300 | // Option returns the originally constructed Transformer option.
301 | // The == operator can be used to detect the exact option used.
302 | func (tf Transform) Option() Option { return tf.trans }
303 |
304 | // isExported reports whether the identifier is exported.
305 | func isExported(id string) bool {
306 | r, _ := utf8.DecodeRuneInString(id)
307 | return unicode.IsUpper(r)
308 | }
309 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/report.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp
6 |
7 | // defaultReporter implements the reporter interface.
8 | //
9 | // As Equal serially calls the PushStep, Report, and PopStep methods, the
10 | // defaultReporter constructs a tree-based representation of the compared value
11 | // and the result of each comparison (see valueNode).
12 | //
13 | // When the String method is called, the FormatDiff method transforms the
14 | // valueNode tree into a textNode tree, which is a tree-based representation
15 | // of the textual output (see textNode).
16 | //
17 | // Lastly, the textNode.String method produces the final report as a string.
18 | type defaultReporter struct {
19 | root *valueNode
20 | curr *valueNode
21 | }
22 |
23 | func (r *defaultReporter) PushStep(ps PathStep) {
24 | r.curr = r.curr.PushStep(ps)
25 | if r.root == nil {
26 | r.root = r.curr
27 | }
28 | }
29 | func (r *defaultReporter) Report(rs Result) {
30 | r.curr.Report(rs)
31 | }
32 | func (r *defaultReporter) PopStep() {
33 | r.curr = r.curr.PopStep()
34 | }
35 |
36 | // String provides a full report of the differences detected as a structured
37 | // literal in pseudo-Go syntax. String may only be called after the entire tree
38 | // has been traversed.
39 | func (r *defaultReporter) String() string {
40 | assert(r.root != nil && r.curr == nil)
41 | if r.root.NumDiff == 0 {
42 | return ""
43 | }
44 | return formatOptions{}.FormatDiff(r.root).String()
45 | }
46 |
47 | func assert(ok bool) {
48 | if !ok {
49 | panic("assertion failure")
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/report_compare.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp
6 |
7 | import (
8 | "fmt"
9 | "reflect"
10 |
11 | "github.com/google/go-cmp/cmp/internal/value"
12 | )
13 |
14 | // TODO: Enforce limits?
15 | // * Enforce maximum number of records to print per node?
16 | // * Enforce maximum size in bytes allowed?
17 | // * As a heuristic, use less verbosity for equal nodes than unequal nodes.
18 | // TODO: Enforce unique outputs?
19 | // * Avoid Stringer methods if it results in same output?
20 | // * Print pointer address if outputs still equal?
21 |
22 | // numContextRecords is the number of surrounding equal records to print.
23 | const numContextRecords = 2
24 |
25 | type diffMode byte
26 |
27 | const (
28 | diffUnknown diffMode = 0
29 | diffIdentical diffMode = ' '
30 | diffRemoved diffMode = '-'
31 | diffInserted diffMode = '+'
32 | )
33 |
34 | type typeMode int
35 |
36 | const (
37 | // emitType always prints the type.
38 | emitType typeMode = iota
39 | // elideType never prints the type.
40 | elideType
41 | // autoType prints the type only for composite kinds
42 | // (i.e., structs, slices, arrays, and maps).
43 | autoType
44 | )
45 |
46 | type formatOptions struct {
47 | // DiffMode controls the output mode of FormatDiff.
48 | //
49 | // If diffUnknown, then produce a diff of the x and y values.
50 | // If diffIdentical, then emit values as if they were equal.
51 | // If diffRemoved, then only emit x values (ignoring y values).
52 | // If diffInserted, then only emit y values (ignoring x values).
53 | DiffMode diffMode
54 |
55 | // TypeMode controls whether to print the type for the current node.
56 | //
57 | // As a general rule of thumb, we always print the type of the next node
58 | // after an interface, and always elide the type of the next node after
59 | // a slice or map node.
60 | TypeMode typeMode
61 |
62 | // formatValueOptions are options specific to printing reflect.Values.
63 | formatValueOptions
64 | }
65 |
66 | func (opts formatOptions) WithDiffMode(d diffMode) formatOptions {
67 | opts.DiffMode = d
68 | return opts
69 | }
70 | func (opts formatOptions) WithTypeMode(t typeMode) formatOptions {
71 | opts.TypeMode = t
72 | return opts
73 | }
74 |
75 | // FormatDiff converts a valueNode tree into a textNode tree, where the later
76 | // is a textual representation of the differences detected in the former.
77 | func (opts formatOptions) FormatDiff(v *valueNode) textNode {
78 | // Check whether we have specialized formatting for this node.
79 | // This is not necessary, but helpful for producing more readable outputs.
80 | if opts.CanFormatDiffSlice(v) {
81 | return opts.FormatDiffSlice(v)
82 | }
83 |
84 | // For leaf nodes, format the value based on the reflect.Values alone.
85 | if v.MaxDepth == 0 {
86 | switch opts.DiffMode {
87 | case diffUnknown, diffIdentical:
88 | // Format Equal.
89 | if v.NumDiff == 0 {
90 | outx := opts.FormatValue(v.ValueX, visitedPointers{})
91 | outy := opts.FormatValue(v.ValueY, visitedPointers{})
92 | if v.NumIgnored > 0 && v.NumSame == 0 {
93 | return textEllipsis
94 | } else if outx.Len() < outy.Len() {
95 | return outx
96 | } else {
97 | return outy
98 | }
99 | }
100 |
101 | // Format unequal.
102 | assert(opts.DiffMode == diffUnknown)
103 | var list textList
104 | outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, visitedPointers{})
105 | outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, visitedPointers{})
106 | if outx != nil {
107 | list = append(list, textRecord{Diff: '-', Value: outx})
108 | }
109 | if outy != nil {
110 | list = append(list, textRecord{Diff: '+', Value: outy})
111 | }
112 | return opts.WithTypeMode(emitType).FormatType(v.Type, list)
113 | case diffRemoved:
114 | return opts.FormatValue(v.ValueX, visitedPointers{})
115 | case diffInserted:
116 | return opts.FormatValue(v.ValueY, visitedPointers{})
117 | default:
118 | panic("invalid diff mode")
119 | }
120 | }
121 |
122 | // Descend into the child value node.
123 | if v.TransformerName != "" {
124 | out := opts.WithTypeMode(emitType).FormatDiff(v.Value)
125 | out = textWrap{"Inverse(" + v.TransformerName + ", ", out, ")"}
126 | return opts.FormatType(v.Type, out)
127 | } else {
128 | switch k := v.Type.Kind(); k {
129 | case reflect.Struct, reflect.Array, reflect.Slice, reflect.Map:
130 | return opts.FormatType(v.Type, opts.formatDiffList(v.Records, k))
131 | case reflect.Ptr:
132 | return textWrap{"&", opts.FormatDiff(v.Value), ""}
133 | case reflect.Interface:
134 | return opts.WithTypeMode(emitType).FormatDiff(v.Value)
135 | default:
136 | panic(fmt.Sprintf("%v cannot have children", k))
137 | }
138 | }
139 | }
140 |
141 | func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) textNode {
142 | // Derive record name based on the data structure kind.
143 | var name string
144 | var formatKey func(reflect.Value) string
145 | switch k {
146 | case reflect.Struct:
147 | name = "field"
148 | opts = opts.WithTypeMode(autoType)
149 | formatKey = func(v reflect.Value) string { return v.String() }
150 | case reflect.Slice, reflect.Array:
151 | name = "element"
152 | opts = opts.WithTypeMode(elideType)
153 | formatKey = func(reflect.Value) string { return "" }
154 | case reflect.Map:
155 | name = "entry"
156 | opts = opts.WithTypeMode(elideType)
157 | formatKey = formatMapKey
158 | }
159 |
160 | // Handle unification.
161 | switch opts.DiffMode {
162 | case diffIdentical, diffRemoved, diffInserted:
163 | var list textList
164 | var deferredEllipsis bool // Add final "..." to indicate records were dropped
165 | for _, r := range recs {
166 | // Elide struct fields that are zero value.
167 | if k == reflect.Struct {
168 | var isZero bool
169 | switch opts.DiffMode {
170 | case diffIdentical:
171 | isZero = value.IsZero(r.Value.ValueX) || value.IsZero(r.Value.ValueY)
172 | case diffRemoved:
173 | isZero = value.IsZero(r.Value.ValueX)
174 | case diffInserted:
175 | isZero = value.IsZero(r.Value.ValueY)
176 | }
177 | if isZero {
178 | continue
179 | }
180 | }
181 | // Elide ignored nodes.
182 | if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 {
183 | deferredEllipsis = !(k == reflect.Slice || k == reflect.Array)
184 | if !deferredEllipsis {
185 | list.AppendEllipsis(diffStats{})
186 | }
187 | continue
188 | }
189 | if out := opts.FormatDiff(r.Value); out != nil {
190 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
191 | }
192 | }
193 | if deferredEllipsis {
194 | list.AppendEllipsis(diffStats{})
195 | }
196 | return textWrap{"{", list, "}"}
197 | case diffUnknown:
198 | default:
199 | panic("invalid diff mode")
200 | }
201 |
202 | // Handle differencing.
203 | var list textList
204 | groups := coalesceAdjacentRecords(name, recs)
205 | for i, ds := range groups {
206 | // Handle equal records.
207 | if ds.NumDiff() == 0 {
208 | // Compute the number of leading and trailing records to print.
209 | var numLo, numHi int
210 | numEqual := ds.NumIgnored + ds.NumIdentical
211 | for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 {
212 | if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 {
213 | break
214 | }
215 | numLo++
216 | }
217 | for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 {
218 | if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 {
219 | break
220 | }
221 | numHi++
222 | }
223 | if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 {
224 | numHi++ // Avoid pointless coalescing of a single equal record
225 | }
226 |
227 | // Format the equal values.
228 | for _, r := range recs[:numLo] {
229 | out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value)
230 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
231 | }
232 | if numEqual > numLo+numHi {
233 | ds.NumIdentical -= numLo + numHi
234 | list.AppendEllipsis(ds)
235 | }
236 | for _, r := range recs[numEqual-numHi : numEqual] {
237 | out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value)
238 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
239 | }
240 | recs = recs[numEqual:]
241 | continue
242 | }
243 |
244 | // Handle unequal records.
245 | for _, r := range recs[:ds.NumDiff()] {
246 | switch {
247 | case opts.CanFormatDiffSlice(r.Value):
248 | out := opts.FormatDiffSlice(r.Value)
249 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
250 | case r.Value.NumChildren == r.Value.MaxDepth:
251 | outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value)
252 | outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value)
253 | if outx != nil {
254 | list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx})
255 | }
256 | if outy != nil {
257 | list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy})
258 | }
259 | default:
260 | out := opts.FormatDiff(r.Value)
261 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
262 | }
263 | }
264 | recs = recs[ds.NumDiff():]
265 | }
266 | assert(len(recs) == 0)
267 | return textWrap{"{", list, "}"}
268 | }
269 |
270 | // coalesceAdjacentRecords coalesces the list of records into groups of
271 | // adjacent equal, or unequal counts.
272 | func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) {
273 | var prevCase int // Arbitrary index into which case last occurred
274 | lastStats := func(i int) *diffStats {
275 | if prevCase != i {
276 | groups = append(groups, diffStats{Name: name})
277 | prevCase = i
278 | }
279 | return &groups[len(groups)-1]
280 | }
281 | for _, r := range recs {
282 | switch rv := r.Value; {
283 | case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0:
284 | lastStats(1).NumIgnored++
285 | case rv.NumDiff == 0:
286 | lastStats(1).NumIdentical++
287 | case rv.NumDiff > 0 && !rv.ValueY.IsValid():
288 | lastStats(2).NumRemoved++
289 | case rv.NumDiff > 0 && !rv.ValueX.IsValid():
290 | lastStats(2).NumInserted++
291 | default:
292 | lastStats(2).NumModified++
293 | }
294 | }
295 | return groups
296 | }
297 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/report_reflect.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp
6 |
7 | import (
8 | "fmt"
9 | "reflect"
10 | "strconv"
11 | "strings"
12 | "unicode"
13 |
14 | "github.com/google/go-cmp/cmp/internal/flags"
15 | "github.com/google/go-cmp/cmp/internal/value"
16 | )
17 |
18 | type formatValueOptions struct {
19 | // AvoidStringer controls whether to avoid calling custom stringer
20 | // methods like error.Error or fmt.Stringer.String.
21 | AvoidStringer bool
22 |
23 | // ShallowPointers controls whether to avoid descending into pointers.
24 | // Useful when printing map keys, where pointer comparison is performed
25 | // on the pointer address rather than the pointed-at value.
26 | ShallowPointers bool
27 |
28 | // PrintAddresses controls whether to print the address of all pointers,
29 | // slice elements, and maps.
30 | PrintAddresses bool
31 | }
32 |
33 | // FormatType prints the type as if it were wrapping s.
34 | // This may return s as-is depending on the current type and TypeMode mode.
35 | func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
36 | // Check whether to emit the type or not.
37 | switch opts.TypeMode {
38 | case autoType:
39 | switch t.Kind() {
40 | case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
41 | if s.Equal(textNil) {
42 | return s
43 | }
44 | default:
45 | return s
46 | }
47 | case elideType:
48 | return s
49 | }
50 |
51 | // Determine the type label, applying special handling for unnamed types.
52 | typeName := t.String()
53 | if t.Name() == "" {
54 | // According to Go grammar, certain type literals contain symbols that
55 | // do not strongly bind to the next lexicographical token (e.g., *T).
56 | switch t.Kind() {
57 | case reflect.Chan, reflect.Func, reflect.Ptr:
58 | typeName = "(" + typeName + ")"
59 | }
60 | typeName = strings.Replace(typeName, "struct {", "struct{", -1)
61 | typeName = strings.Replace(typeName, "interface {", "interface{", -1)
62 | }
63 |
64 | // Avoid wrap the value in parenthesis if unnecessary.
65 | if s, ok := s.(textWrap); ok {
66 | hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")")
67 | hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}")
68 | if hasParens || hasBraces {
69 | return textWrap{typeName, s, ""}
70 | }
71 | }
72 | return textWrap{typeName + "(", s, ")"}
73 | }
74 |
75 | // FormatValue prints the reflect.Value, taking extra care to avoid descending
76 | // into pointers already in m. As pointers are visited, m is also updated.
77 | func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
78 | if !v.IsValid() {
79 | return nil
80 | }
81 | t := v.Type()
82 |
83 | // Check whether there is an Error or String method to call.
84 | if !opts.AvoidStringer && v.CanInterface() {
85 | // Avoid calling Error or String methods on nil receivers since many
86 | // implementations crash when doing so.
87 | if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
88 | switch v := v.Interface().(type) {
89 | case error:
90 | return textLine("e" + formatString(v.Error()))
91 | case fmt.Stringer:
92 | return textLine("s" + formatString(v.String()))
93 | }
94 | }
95 | }
96 |
97 | // Check whether to explicitly wrap the result with the type.
98 | var skipType bool
99 | defer func() {
100 | if !skipType {
101 | out = opts.FormatType(t, out)
102 | }
103 | }()
104 |
105 | var ptr string
106 | switch t.Kind() {
107 | case reflect.Bool:
108 | return textLine(fmt.Sprint(v.Bool()))
109 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
110 | return textLine(fmt.Sprint(v.Int()))
111 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
112 | // Unnamed uints are usually bytes or words, so use hexadecimal.
113 | if t.PkgPath() == "" || t.Kind() == reflect.Uintptr {
114 | return textLine(formatHex(v.Uint()))
115 | }
116 | return textLine(fmt.Sprint(v.Uint()))
117 | case reflect.Float32, reflect.Float64:
118 | return textLine(fmt.Sprint(v.Float()))
119 | case reflect.Complex64, reflect.Complex128:
120 | return textLine(fmt.Sprint(v.Complex()))
121 | case reflect.String:
122 | return textLine(formatString(v.String()))
123 | case reflect.UnsafePointer, reflect.Chan, reflect.Func:
124 | return textLine(formatPointer(v))
125 | case reflect.Struct:
126 | var list textList
127 | for i := 0; i < v.NumField(); i++ {
128 | vv := v.Field(i)
129 | if value.IsZero(vv) {
130 | continue // Elide fields with zero values
131 | }
132 | s := opts.WithTypeMode(autoType).FormatValue(vv, m)
133 | list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
134 | }
135 | return textWrap{"{", list, "}"}
136 | case reflect.Slice:
137 | if v.IsNil() {
138 | return textNil
139 | }
140 | if opts.PrintAddresses {
141 | ptr = formatPointer(v)
142 | }
143 | fallthrough
144 | case reflect.Array:
145 | var list textList
146 | for i := 0; i < v.Len(); i++ {
147 | vi := v.Index(i)
148 | if vi.CanAddr() { // Check for cyclic elements
149 | p := vi.Addr()
150 | if m.Visit(p) {
151 | var out textNode
152 | out = textLine(formatPointer(p))
153 | out = opts.WithTypeMode(emitType).FormatType(p.Type(), out)
154 | out = textWrap{"*", out, ""}
155 | list = append(list, textRecord{Value: out})
156 | continue
157 | }
158 | }
159 | s := opts.WithTypeMode(elideType).FormatValue(vi, m)
160 | list = append(list, textRecord{Value: s})
161 | }
162 | return textWrap{ptr + "{", list, "}"}
163 | case reflect.Map:
164 | if v.IsNil() {
165 | return textNil
166 | }
167 | if m.Visit(v) {
168 | return textLine(formatPointer(v))
169 | }
170 |
171 | var list textList
172 | for _, k := range value.SortKeys(v.MapKeys()) {
173 | sk := formatMapKey(k)
174 | sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m)
175 | list = append(list, textRecord{Key: sk, Value: sv})
176 | }
177 | if opts.PrintAddresses {
178 | ptr = formatPointer(v)
179 | }
180 | return textWrap{ptr + "{", list, "}"}
181 | case reflect.Ptr:
182 | if v.IsNil() {
183 | return textNil
184 | }
185 | if m.Visit(v) || opts.ShallowPointers {
186 | return textLine(formatPointer(v))
187 | }
188 | if opts.PrintAddresses {
189 | ptr = formatPointer(v)
190 | }
191 | skipType = true // Let the underlying value print the type instead
192 | return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""}
193 | case reflect.Interface:
194 | if v.IsNil() {
195 | return textNil
196 | }
197 | // Interfaces accept different concrete types,
198 | // so configure the underlying value to explicitly print the type.
199 | skipType = true // Print the concrete type instead
200 | return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m)
201 | default:
202 | panic(fmt.Sprintf("%v kind not handled", v.Kind()))
203 | }
204 | }
205 |
206 | // formatMapKey formats v as if it were a map key.
207 | // The result is guaranteed to be a single line.
208 | func formatMapKey(v reflect.Value) string {
209 | var opts formatOptions
210 | opts.TypeMode = elideType
211 | opts.ShallowPointers = true
212 | s := opts.FormatValue(v, visitedPointers{}).String()
213 | return strings.TrimSpace(s)
214 | }
215 |
216 | // formatString prints s as a double-quoted or backtick-quoted string.
217 | func formatString(s string) string {
218 | // Use quoted string if it the same length as a raw string literal.
219 | // Otherwise, attempt to use the raw string form.
220 | qs := strconv.Quote(s)
221 | if len(qs) == 1+len(s)+1 {
222 | return qs
223 | }
224 |
225 | // Disallow newlines to ensure output is a single line.
226 | // Only allow printable runes for readability purposes.
227 | rawInvalid := func(r rune) bool {
228 | return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
229 | }
230 | if strings.IndexFunc(s, rawInvalid) < 0 {
231 | return "`" + s + "`"
232 | }
233 | return qs
234 | }
235 |
236 | // formatHex prints u as a hexadecimal integer in Go notation.
237 | func formatHex(u uint64) string {
238 | var f string
239 | switch {
240 | case u <= 0xff:
241 | f = "0x%02x"
242 | case u <= 0xffff:
243 | f = "0x%04x"
244 | case u <= 0xffffff:
245 | f = "0x%06x"
246 | case u <= 0xffffffff:
247 | f = "0x%08x"
248 | case u <= 0xffffffffff:
249 | f = "0x%010x"
250 | case u <= 0xffffffffffff:
251 | f = "0x%012x"
252 | case u <= 0xffffffffffffff:
253 | f = "0x%014x"
254 | case u <= 0xffffffffffffffff:
255 | f = "0x%016x"
256 | }
257 | return fmt.Sprintf(f, u)
258 | }
259 |
260 | // formatPointer prints the address of the pointer.
261 | func formatPointer(v reflect.Value) string {
262 | p := v.Pointer()
263 | if flags.Deterministic {
264 | p = 0xdeadf00f // Only used for stable testing purposes
265 | }
266 | return fmt.Sprintf("⟪0x%x⟫", p)
267 | }
268 |
269 | type visitedPointers map[value.Pointer]struct{}
270 |
271 | // Visit inserts pointer v into the visited map and reports whether it had
272 | // already been visited before.
273 | func (m visitedPointers) Visit(v reflect.Value) bool {
274 | p := value.PointerOf(v)
275 | _, visited := m[p]
276 | m[p] = struct{}{}
277 | return visited
278 | }
279 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/report_slices.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp
6 |
7 | import (
8 | "bytes"
9 | "fmt"
10 | "reflect"
11 | "strings"
12 | "unicode"
13 | "unicode/utf8"
14 |
15 | "github.com/google/go-cmp/cmp/internal/diff"
16 | )
17 |
18 | // CanFormatDiffSlice reports whether we support custom formatting for nodes
19 | // that are slices of primitive kinds or strings.
20 | func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
21 | switch {
22 | case opts.DiffMode != diffUnknown:
23 | return false // Must be formatting in diff mode
24 | case v.NumDiff == 0:
25 | return false // No differences detected
26 | case v.NumIgnored+v.NumCompared+v.NumTransformed > 0:
27 | // TODO: Handle the case where someone uses bytes.Equal on a large slice.
28 | return false // Some custom option was used to determined equality
29 | case !v.ValueX.IsValid() || !v.ValueY.IsValid():
30 | return false // Both values must be valid
31 | }
32 |
33 | switch t := v.Type; t.Kind() {
34 | case reflect.String:
35 | case reflect.Array, reflect.Slice:
36 | // Only slices of primitive types have specialized handling.
37 | switch t.Elem().Kind() {
38 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
39 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
40 | reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
41 | default:
42 | return false
43 | }
44 |
45 | // If a sufficient number of elements already differ,
46 | // use specialized formatting even if length requirement is not met.
47 | if v.NumDiff > v.NumSame {
48 | return true
49 | }
50 | default:
51 | return false
52 | }
53 |
54 | // Use specialized string diffing for longer slices or strings.
55 | const minLength = 64
56 | return v.ValueX.Len() >= minLength && v.ValueY.Len() >= minLength
57 | }
58 |
59 | // FormatDiffSlice prints a diff for the slices (or strings) represented by v.
60 | // This provides custom-tailored logic to make printing of differences in
61 | // textual strings and slices of primitive kinds more readable.
62 | func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
63 | assert(opts.DiffMode == diffUnknown)
64 | t, vx, vy := v.Type, v.ValueX, v.ValueY
65 |
66 | // Auto-detect the type of the data.
67 | var isLinedText, isText, isBinary bool
68 | var sx, sy string
69 | switch {
70 | case t.Kind() == reflect.String:
71 | sx, sy = vx.String(), vy.String()
72 | isText = true // Initial estimate, verify later
73 | case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)):
74 | sx, sy = string(vx.Bytes()), string(vy.Bytes())
75 | isBinary = true // Initial estimate, verify later
76 | case t.Kind() == reflect.Array:
77 | // Arrays need to be addressable for slice operations to work.
78 | vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem()
79 | vx2.Set(vx)
80 | vy2.Set(vy)
81 | vx, vy = vx2, vy2
82 | }
83 | if isText || isBinary {
84 | var numLines, lastLineIdx, maxLineLen int
85 | isBinary = false
86 | for i, r := range sx + sy {
87 | if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError {
88 | isBinary = true
89 | break
90 | }
91 | if r == '\n' {
92 | if maxLineLen < i-lastLineIdx {
93 | maxLineLen = i - lastLineIdx
94 | }
95 | lastLineIdx = i + 1
96 | numLines++
97 | }
98 | }
99 | isText = !isBinary
100 | isLinedText = isText && numLines >= 4 && maxLineLen <= 256
101 | }
102 |
103 | // Format the string into printable records.
104 | var list textList
105 | var delim string
106 | switch {
107 | // If the text appears to be multi-lined text,
108 | // then perform differencing across individual lines.
109 | case isLinedText:
110 | ssx := strings.Split(sx, "\n")
111 | ssy := strings.Split(sy, "\n")
112 | list = opts.formatDiffSlice(
113 | reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line",
114 | func(v reflect.Value, d diffMode) textRecord {
115 | s := formatString(v.Index(0).String())
116 | return textRecord{Diff: d, Value: textLine(s)}
117 | },
118 | )
119 | delim = "\n"
120 | // If the text appears to be single-lined text,
121 | // then perform differencing in approximately fixed-sized chunks.
122 | // The output is printed as quoted strings.
123 | case isText:
124 | list = opts.formatDiffSlice(
125 | reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte",
126 | func(v reflect.Value, d diffMode) textRecord {
127 | s := formatString(v.String())
128 | return textRecord{Diff: d, Value: textLine(s)}
129 | },
130 | )
131 | delim = ""
132 | // If the text appears to be binary data,
133 | // then perform differencing in approximately fixed-sized chunks.
134 | // The output is inspired by hexdump.
135 | case isBinary:
136 | list = opts.formatDiffSlice(
137 | reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte",
138 | func(v reflect.Value, d diffMode) textRecord {
139 | var ss []string
140 | for i := 0; i < v.Len(); i++ {
141 | ss = append(ss, formatHex(v.Index(i).Uint()))
142 | }
143 | s := strings.Join(ss, ", ")
144 | comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String())))
145 | return textRecord{Diff: d, Value: textLine(s), Comment: comment}
146 | },
147 | )
148 | // For all other slices of primitive types,
149 | // then perform differencing in approximately fixed-sized chunks.
150 | // The size of each chunk depends on the width of the element kind.
151 | default:
152 | var chunkSize int
153 | if t.Elem().Kind() == reflect.Bool {
154 | chunkSize = 16
155 | } else {
156 | switch t.Elem().Bits() {
157 | case 8:
158 | chunkSize = 16
159 | case 16:
160 | chunkSize = 12
161 | case 32:
162 | chunkSize = 8
163 | default:
164 | chunkSize = 8
165 | }
166 | }
167 | list = opts.formatDiffSlice(
168 | vx, vy, chunkSize, t.Elem().Kind().String(),
169 | func(v reflect.Value, d diffMode) textRecord {
170 | var ss []string
171 | for i := 0; i < v.Len(); i++ {
172 | switch t.Elem().Kind() {
173 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
174 | ss = append(ss, fmt.Sprint(v.Index(i).Int()))
175 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
176 | ss = append(ss, formatHex(v.Index(i).Uint()))
177 | case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
178 | ss = append(ss, fmt.Sprint(v.Index(i).Interface()))
179 | }
180 | }
181 | s := strings.Join(ss, ", ")
182 | return textRecord{Diff: d, Value: textLine(s)}
183 | },
184 | )
185 | }
186 |
187 | // Wrap the output with appropriate type information.
188 | var out textNode = textWrap{"{", list, "}"}
189 | if !isText {
190 | // The "{...}" byte-sequence literal is not valid Go syntax for strings.
191 | // Emit the type for extra clarity (e.g. "string{...}").
192 | if t.Kind() == reflect.String {
193 | opts = opts.WithTypeMode(emitType)
194 | }
195 | return opts.FormatType(t, out)
196 | }
197 | switch t.Kind() {
198 | case reflect.String:
199 | out = textWrap{"strings.Join(", out, fmt.Sprintf(", %q)", delim)}
200 | if t != reflect.TypeOf(string("")) {
201 | out = opts.FormatType(t, out)
202 | }
203 | case reflect.Slice:
204 | out = textWrap{"bytes.Join(", out, fmt.Sprintf(", %q)", delim)}
205 | if t != reflect.TypeOf([]byte(nil)) {
206 | out = opts.FormatType(t, out)
207 | }
208 | }
209 | return out
210 | }
211 |
212 | // formatASCII formats s as an ASCII string.
213 | // This is useful for printing binary strings in a semi-legible way.
214 | func formatASCII(s string) string {
215 | b := bytes.Repeat([]byte{'.'}, len(s))
216 | for i := 0; i < len(s); i++ {
217 | if ' ' <= s[i] && s[i] <= '~' {
218 | b[i] = s[i]
219 | }
220 | }
221 | return string(b)
222 | }
223 |
224 | func (opts formatOptions) formatDiffSlice(
225 | vx, vy reflect.Value, chunkSize int, name string,
226 | makeRec func(reflect.Value, diffMode) textRecord,
227 | ) (list textList) {
228 | es := diff.Difference(vx.Len(), vy.Len(), func(ix int, iy int) diff.Result {
229 | return diff.BoolResult(vx.Index(ix).Interface() == vy.Index(iy).Interface())
230 | })
231 |
232 | appendChunks := func(v reflect.Value, d diffMode) int {
233 | n0 := v.Len()
234 | for v.Len() > 0 {
235 | n := chunkSize
236 | if n > v.Len() {
237 | n = v.Len()
238 | }
239 | list = append(list, makeRec(v.Slice(0, n), d))
240 | v = v.Slice(n, v.Len())
241 | }
242 | return n0 - v.Len()
243 | }
244 |
245 | groups := coalesceAdjacentEdits(name, es)
246 | groups = coalesceInterveningIdentical(groups, chunkSize/4)
247 | for i, ds := range groups {
248 | // Print equal.
249 | if ds.NumDiff() == 0 {
250 | // Compute the number of leading and trailing equal bytes to print.
251 | var numLo, numHi int
252 | numEqual := ds.NumIgnored + ds.NumIdentical
253 | for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 {
254 | numLo++
255 | }
256 | for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 {
257 | numHi++
258 | }
259 | if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 {
260 | numHi = numEqual - numLo // Avoid pointless coalescing of single equal row
261 | }
262 |
263 | // Print the equal bytes.
264 | appendChunks(vx.Slice(0, numLo), diffIdentical)
265 | if numEqual > numLo+numHi {
266 | ds.NumIdentical -= numLo + numHi
267 | list.AppendEllipsis(ds)
268 | }
269 | appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical)
270 | vx = vx.Slice(numEqual, vx.Len())
271 | vy = vy.Slice(numEqual, vy.Len())
272 | continue
273 | }
274 |
275 | // Print unequal.
276 | nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved)
277 | vx = vx.Slice(nx, vx.Len())
278 | ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted)
279 | vy = vy.Slice(ny, vy.Len())
280 | }
281 | assert(vx.Len() == 0 && vy.Len() == 0)
282 | return list
283 | }
284 |
285 | // coalesceAdjacentEdits coalesces the list of edits into groups of adjacent
286 | // equal or unequal counts.
287 | func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) {
288 | var prevCase int // Arbitrary index into which case last occurred
289 | lastStats := func(i int) *diffStats {
290 | if prevCase != i {
291 | groups = append(groups, diffStats{Name: name})
292 | prevCase = i
293 | }
294 | return &groups[len(groups)-1]
295 | }
296 | for _, e := range es {
297 | switch e {
298 | case diff.Identity:
299 | lastStats(1).NumIdentical++
300 | case diff.UniqueX:
301 | lastStats(2).NumRemoved++
302 | case diff.UniqueY:
303 | lastStats(2).NumInserted++
304 | case diff.Modified:
305 | lastStats(2).NumModified++
306 | }
307 | }
308 | return groups
309 | }
310 |
311 | // coalesceInterveningIdentical coalesces sufficiently short (<= windowSize)
312 | // equal groups into adjacent unequal groups that currently result in a
313 | // dual inserted/removed printout. This acts as a high-pass filter to smooth
314 | // out high-frequency changes within the windowSize.
315 | func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats {
316 | groups, groupsOrig := groups[:0], groups
317 | for i, ds := range groupsOrig {
318 | if len(groups) >= 2 && ds.NumDiff() > 0 {
319 | prev := &groups[len(groups)-2] // Unequal group
320 | curr := &groups[len(groups)-1] // Equal group
321 | next := &groupsOrig[i] // Unequal group
322 | hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0
323 | hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0
324 | if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize {
325 | *prev = prev.Append(*curr).Append(*next)
326 | groups = groups[:len(groups)-1] // Truncate off equal group
327 | continue
328 | }
329 | }
330 | groups = append(groups, ds)
331 | }
332 | return groups
333 | }
334 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/report_text.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp
6 |
7 | import (
8 | "bytes"
9 | "fmt"
10 | "math/rand"
11 | "strings"
12 | "time"
13 |
14 | "github.com/google/go-cmp/cmp/internal/flags"
15 | )
16 |
17 | var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
18 |
19 | type indentMode int
20 |
21 | func (n indentMode) appendIndent(b []byte, d diffMode) []byte {
22 | // The output of Diff is documented as being unstable to provide future
23 | // flexibility in changing the output for more humanly readable reports.
24 | // This logic intentionally introduces instability to the exact output
25 | // so that users can detect accidental reliance on stability early on,
26 | // rather than much later when an actual change to the format occurs.
27 | if flags.Deterministic || randBool {
28 | // Use regular spaces (U+0020).
29 | switch d {
30 | case diffUnknown, diffIdentical:
31 | b = append(b, " "...)
32 | case diffRemoved:
33 | b = append(b, "- "...)
34 | case diffInserted:
35 | b = append(b, "+ "...)
36 | }
37 | } else {
38 | // Use non-breaking spaces (U+00a0).
39 | switch d {
40 | case diffUnknown, diffIdentical:
41 | b = append(b, " "...)
42 | case diffRemoved:
43 | b = append(b, "- "...)
44 | case diffInserted:
45 | b = append(b, "+ "...)
46 | }
47 | }
48 | return repeatCount(n).appendChar(b, '\t')
49 | }
50 |
51 | type repeatCount int
52 |
53 | func (n repeatCount) appendChar(b []byte, c byte) []byte {
54 | for ; n > 0; n-- {
55 | b = append(b, c)
56 | }
57 | return b
58 | }
59 |
60 | // textNode is a simplified tree-based representation of structured text.
61 | // Possible node types are textWrap, textList, or textLine.
62 | type textNode interface {
63 | // Len reports the length in bytes of a single-line version of the tree.
64 | // Nested textRecord.Diff and textRecord.Comment fields are ignored.
65 | Len() int
66 | // Equal reports whether the two trees are structurally identical.
67 | // Nested textRecord.Diff and textRecord.Comment fields are compared.
68 | Equal(textNode) bool
69 | // String returns the string representation of the text tree.
70 | // It is not guaranteed that len(x.String()) == x.Len(),
71 | // nor that x.String() == y.String() implies that x.Equal(y).
72 | String() string
73 |
74 | // formatCompactTo formats the contents of the tree as a single-line string
75 | // to the provided buffer. Any nested textRecord.Diff and textRecord.Comment
76 | // fields are ignored.
77 | //
78 | // However, not all nodes in the tree should be collapsed as a single-line.
79 | // If a node can be collapsed as a single-line, it is replaced by a textLine
80 | // node. Since the top-level node cannot replace itself, this also returns
81 | // the current node itself.
82 | //
83 | // This does not mutate the receiver.
84 | formatCompactTo([]byte, diffMode) ([]byte, textNode)
85 | // formatExpandedTo formats the contents of the tree as a multi-line string
86 | // to the provided buffer. In order for column alignment to operate well,
87 | // formatCompactTo must be called before calling formatExpandedTo.
88 | formatExpandedTo([]byte, diffMode, indentMode) []byte
89 | }
90 |
91 | // textWrap is a wrapper that concatenates a prefix and/or a suffix
92 | // to the underlying node.
93 | type textWrap struct {
94 | Prefix string // e.g., "bytes.Buffer{"
95 | Value textNode // textWrap | textList | textLine
96 | Suffix string // e.g., "}"
97 | }
98 |
99 | func (s textWrap) Len() int {
100 | return len(s.Prefix) + s.Value.Len() + len(s.Suffix)
101 | }
102 | func (s1 textWrap) Equal(s2 textNode) bool {
103 | if s2, ok := s2.(textWrap); ok {
104 | return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix
105 | }
106 | return false
107 | }
108 | func (s textWrap) String() string {
109 | var d diffMode
110 | var n indentMode
111 | _, s2 := s.formatCompactTo(nil, d)
112 | b := n.appendIndent(nil, d) // Leading indent
113 | b = s2.formatExpandedTo(b, d, n) // Main body
114 | b = append(b, '\n') // Trailing newline
115 | return string(b)
116 | }
117 | func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
118 | n0 := len(b) // Original buffer length
119 | b = append(b, s.Prefix...)
120 | b, s.Value = s.Value.formatCompactTo(b, d)
121 | b = append(b, s.Suffix...)
122 | if _, ok := s.Value.(textLine); ok {
123 | return b, textLine(b[n0:])
124 | }
125 | return b, s
126 | }
127 | func (s textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
128 | b = append(b, s.Prefix...)
129 | b = s.Value.formatExpandedTo(b, d, n)
130 | b = append(b, s.Suffix...)
131 | return b
132 | }
133 |
134 | // textList is a comma-separated list of textWrap or textLine nodes.
135 | // The list may be formatted as multi-lines or single-line at the discretion
136 | // of the textList.formatCompactTo method.
137 | type textList []textRecord
138 | type textRecord struct {
139 | Diff diffMode // e.g., 0 or '-' or '+'
140 | Key string // e.g., "MyField"
141 | Value textNode // textWrap | textLine
142 | Comment fmt.Stringer // e.g., "6 identical fields"
143 | }
144 |
145 | // AppendEllipsis appends a new ellipsis node to the list if none already
146 | // exists at the end. If cs is non-zero it coalesces the statistics with the
147 | // previous diffStats.
148 | func (s *textList) AppendEllipsis(ds diffStats) {
149 | hasStats := ds != diffStats{}
150 | if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) {
151 | if hasStats {
152 | *s = append(*s, textRecord{Value: textEllipsis, Comment: ds})
153 | } else {
154 | *s = append(*s, textRecord{Value: textEllipsis})
155 | }
156 | return
157 | }
158 | if hasStats {
159 | (*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds)
160 | }
161 | }
162 |
163 | func (s textList) Len() (n int) {
164 | for i, r := range s {
165 | n += len(r.Key)
166 | if r.Key != "" {
167 | n += len(": ")
168 | }
169 | n += r.Value.Len()
170 | if i < len(s)-1 {
171 | n += len(", ")
172 | }
173 | }
174 | return n
175 | }
176 |
177 | func (s1 textList) Equal(s2 textNode) bool {
178 | if s2, ok := s2.(textList); ok {
179 | if len(s1) != len(s2) {
180 | return false
181 | }
182 | for i := range s1 {
183 | r1, r2 := s1[i], s2[i]
184 | if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) {
185 | return false
186 | }
187 | }
188 | return true
189 | }
190 | return false
191 | }
192 |
193 | func (s textList) String() string {
194 | return textWrap{"{", s, "}"}.String()
195 | }
196 |
197 | func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
198 | s = append(textList(nil), s...) // Avoid mutating original
199 |
200 | // Determine whether we can collapse this list as a single line.
201 | n0 := len(b) // Original buffer length
202 | var multiLine bool
203 | for i, r := range s {
204 | if r.Diff == diffInserted || r.Diff == diffRemoved {
205 | multiLine = true
206 | }
207 | b = append(b, r.Key...)
208 | if r.Key != "" {
209 | b = append(b, ": "...)
210 | }
211 | b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff)
212 | if _, ok := s[i].Value.(textLine); !ok {
213 | multiLine = true
214 | }
215 | if r.Comment != nil {
216 | multiLine = true
217 | }
218 | if i < len(s)-1 {
219 | b = append(b, ", "...)
220 | }
221 | }
222 | // Force multi-lined output when printing a removed/inserted node that
223 | // is sufficiently long.
224 | if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > 80 {
225 | multiLine = true
226 | }
227 | if !multiLine {
228 | return b, textLine(b[n0:])
229 | }
230 | return b, s
231 | }
232 |
233 | func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
234 | alignKeyLens := s.alignLens(
235 | func(r textRecord) bool {
236 | _, isLine := r.Value.(textLine)
237 | return r.Key == "" || !isLine
238 | },
239 | func(r textRecord) int { return len(r.Key) },
240 | )
241 | alignValueLens := s.alignLens(
242 | func(r textRecord) bool {
243 | _, isLine := r.Value.(textLine)
244 | return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil
245 | },
246 | func(r textRecord) int { return len(r.Value.(textLine)) },
247 | )
248 |
249 | // Format the list as a multi-lined output.
250 | n++
251 | for i, r := range s {
252 | b = n.appendIndent(append(b, '\n'), d|r.Diff)
253 | if r.Key != "" {
254 | b = append(b, r.Key+": "...)
255 | }
256 | b = alignKeyLens[i].appendChar(b, ' ')
257 |
258 | b = r.Value.formatExpandedTo(b, d|r.Diff, n)
259 | if !r.Value.Equal(textEllipsis) {
260 | b = append(b, ',')
261 | }
262 | b = alignValueLens[i].appendChar(b, ' ')
263 |
264 | if r.Comment != nil {
265 | b = append(b, " // "+r.Comment.String()...)
266 | }
267 | }
268 | n--
269 |
270 | return n.appendIndent(append(b, '\n'), d)
271 | }
272 |
273 | func (s textList) alignLens(
274 | skipFunc func(textRecord) bool,
275 | lenFunc func(textRecord) int,
276 | ) []repeatCount {
277 | var startIdx, endIdx, maxLen int
278 | lens := make([]repeatCount, len(s))
279 | for i, r := range s {
280 | if skipFunc(r) {
281 | for j := startIdx; j < endIdx && j < len(s); j++ {
282 | lens[j] = repeatCount(maxLen - lenFunc(s[j]))
283 | }
284 | startIdx, endIdx, maxLen = i+1, i+1, 0
285 | } else {
286 | if maxLen < lenFunc(r) {
287 | maxLen = lenFunc(r)
288 | }
289 | endIdx = i + 1
290 | }
291 | }
292 | for j := startIdx; j < endIdx && j < len(s); j++ {
293 | lens[j] = repeatCount(maxLen - lenFunc(s[j]))
294 | }
295 | return lens
296 | }
297 |
298 | // textLine is a single-line segment of text and is always a leaf node
299 | // in the textNode tree.
300 | type textLine []byte
301 |
302 | var (
303 | textNil = textLine("nil")
304 | textEllipsis = textLine("...")
305 | )
306 |
307 | func (s textLine) Len() int {
308 | return len(s)
309 | }
310 | func (s1 textLine) Equal(s2 textNode) bool {
311 | if s2, ok := s2.(textLine); ok {
312 | return bytes.Equal([]byte(s1), []byte(s2))
313 | }
314 | return false
315 | }
316 | func (s textLine) String() string {
317 | return string(s)
318 | }
319 | func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
320 | return append(b, s...), s
321 | }
322 | func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte {
323 | return append(b, s...)
324 | }
325 |
326 | type diffStats struct {
327 | Name string
328 | NumIgnored int
329 | NumIdentical int
330 | NumRemoved int
331 | NumInserted int
332 | NumModified int
333 | }
334 |
335 | func (s diffStats) NumDiff() int {
336 | return s.NumRemoved + s.NumInserted + s.NumModified
337 | }
338 |
339 | func (s diffStats) Append(ds diffStats) diffStats {
340 | assert(s.Name == ds.Name)
341 | s.NumIgnored += ds.NumIgnored
342 | s.NumIdentical += ds.NumIdentical
343 | s.NumRemoved += ds.NumRemoved
344 | s.NumInserted += ds.NumInserted
345 | s.NumModified += ds.NumModified
346 | return s
347 | }
348 |
349 | // String prints a humanly-readable summary of coalesced records.
350 | //
351 | // Example:
352 | // diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields"
353 | func (s diffStats) String() string {
354 | var ss []string
355 | var sum int
356 | labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"}
357 | counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified}
358 | for i, n := range counts {
359 | if n > 0 {
360 | ss = append(ss, fmt.Sprintf("%d %v", n, labels[i]))
361 | }
362 | sum += n
363 | }
364 |
365 | // Pluralize the name (adjusting for some obscure English grammar rules).
366 | name := s.Name
367 | if sum > 1 {
368 | name += "s"
369 | if strings.HasSuffix(name, "ys") {
370 | name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries"
371 | }
372 | }
373 |
374 | // Format the list according to English grammar (with Oxford comma).
375 | switch n := len(ss); n {
376 | case 0:
377 | return ""
378 | case 1, 2:
379 | return strings.Join(ss, " and ") + " " + name
380 | default:
381 | return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name
382 | }
383 | }
384 |
385 | type commentString string
386 |
387 | func (s commentString) String() string { return string(s) }
388 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/cmp/report_value.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019, The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE.md file.
4 |
5 | package cmp
6 |
7 | import "reflect"
8 |
9 | // valueNode represents a single node within a report, which is a
10 | // structured representation of the value tree, containing information
11 | // regarding which nodes are equal or not.
12 | type valueNode struct {
13 | parent *valueNode
14 |
15 | Type reflect.Type
16 | ValueX reflect.Value
17 | ValueY reflect.Value
18 |
19 | // NumSame is the number of leaf nodes that are equal.
20 | // All descendants are equal only if NumDiff is 0.
21 | NumSame int
22 | // NumDiff is the number of leaf nodes that are not equal.
23 | NumDiff int
24 | // NumIgnored is the number of leaf nodes that are ignored.
25 | NumIgnored int
26 | // NumCompared is the number of leaf nodes that were compared
27 | // using an Equal method or Comparer function.
28 | NumCompared int
29 | // NumTransformed is the number of non-leaf nodes that were transformed.
30 | NumTransformed int
31 | // NumChildren is the number of transitive descendants of this node.
32 | // This counts from zero; thus, leaf nodes have no descendants.
33 | NumChildren int
34 | // MaxDepth is the maximum depth of the tree. This counts from zero;
35 | // thus, leaf nodes have a depth of zero.
36 | MaxDepth int
37 |
38 | // Records is a list of struct fields, slice elements, or map entries.
39 | Records []reportRecord // If populated, implies Value is not populated
40 |
41 | // Value is the result of a transformation, pointer indirect, of
42 | // type assertion.
43 | Value *valueNode // If populated, implies Records is not populated
44 |
45 | // TransformerName is the name of the transformer.
46 | TransformerName string // If non-empty, implies Value is populated
47 | }
48 | type reportRecord struct {
49 | Key reflect.Value // Invalid for slice element
50 | Value *valueNode
51 | }
52 |
53 | func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) {
54 | vx, vy := ps.Values()
55 | child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy}
56 | switch s := ps.(type) {
57 | case StructField:
58 | assert(parent.Value == nil)
59 | parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child})
60 | case SliceIndex:
61 | assert(parent.Value == nil)
62 | parent.Records = append(parent.Records, reportRecord{Value: child})
63 | case MapIndex:
64 | assert(parent.Value == nil)
65 | parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child})
66 | case Indirect:
67 | assert(parent.Value == nil && parent.Records == nil)
68 | parent.Value = child
69 | case TypeAssertion:
70 | assert(parent.Value == nil && parent.Records == nil)
71 | parent.Value = child
72 | case Transform:
73 | assert(parent.Value == nil && parent.Records == nil)
74 | parent.Value = child
75 | parent.TransformerName = s.Name()
76 | parent.NumTransformed++
77 | default:
78 | assert(parent == nil) // Must be the root step
79 | }
80 | return child
81 | }
82 |
83 | func (r *valueNode) Report(rs Result) {
84 | assert(r.MaxDepth == 0) // May only be called on leaf nodes
85 |
86 | if rs.ByIgnore() {
87 | r.NumIgnored++
88 | } else {
89 | if rs.Equal() {
90 | r.NumSame++
91 | } else {
92 | r.NumDiff++
93 | }
94 | }
95 | assert(r.NumSame+r.NumDiff+r.NumIgnored == 1)
96 |
97 | if rs.ByMethod() {
98 | r.NumCompared++
99 | }
100 | if rs.ByFunc() {
101 | r.NumCompared++
102 | }
103 | assert(r.NumCompared <= 1)
104 | }
105 |
106 | func (child *valueNode) PopStep() (parent *valueNode) {
107 | if child.parent == nil {
108 | return nil
109 | }
110 | parent = child.parent
111 | parent.NumSame += child.NumSame
112 | parent.NumDiff += child.NumDiff
113 | parent.NumIgnored += child.NumIgnored
114 | parent.NumCompared += child.NumCompared
115 | parent.NumTransformed += child.NumTransformed
116 | parent.NumChildren += child.NumChildren + 1
117 | if parent.MaxDepth < child.MaxDepth+1 {
118 | parent.MaxDepth = child.MaxDepth + 1
119 | }
120 | return parent
121 | }
122 |
--------------------------------------------------------------------------------
/vendor/github.com/google/go-cmp/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/google/go-cmp
2 |
3 | go 1.8
4 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go_import_path: github.com/pkg/errors
3 | go:
4 | - 1.4.3
5 | - 1.5.4
6 | - 1.6.2
7 | - 1.7.1
8 | - tip
9 |
10 | script:
11 | - go test -v ./...
12 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Dave Cheney
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/README.md:
--------------------------------------------------------------------------------
1 | # errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors)
2 |
3 | Package errors provides simple error handling primitives.
4 |
5 | `go get github.com/pkg/errors`
6 |
7 | The traditional error handling idiom in Go is roughly akin to
8 | ```go
9 | if err != nil {
10 | return err
11 | }
12 | ```
13 | which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
14 |
15 | ## Adding context to an error
16 |
17 | The errors.Wrap function returns a new error that adds context to the original error. For example
18 | ```go
19 | _, err := ioutil.ReadAll(r)
20 | if err != nil {
21 | return errors.Wrap(err, "read failed")
22 | }
23 | ```
24 | ## Retrieving the cause of an error
25 |
26 | Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
27 | ```go
28 | type causer interface {
29 | Cause() error
30 | }
31 | ```
32 | `errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
33 | ```go
34 | switch err := errors.Cause(err).(type) {
35 | case *MyError:
36 | // handle specifically
37 | default:
38 | // unknown error
39 | }
40 | ```
41 |
42 | [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
43 |
44 | ## Contributing
45 |
46 | We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
47 |
48 | Before proposing a change, please discuss your change by raising an issue.
49 |
50 | ## Licence
51 |
52 | BSD-2-Clause
53 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: build-{build}.{branch}
2 |
3 | clone_folder: C:\gopath\src\github.com\pkg\errors
4 | shallow_clone: true # for startup speed
5 |
6 | environment:
7 | GOPATH: C:\gopath
8 |
9 | platform:
10 | - x64
11 |
12 | # http://www.appveyor.com/docs/installed-software
13 | install:
14 | # some helpful output for debugging builds
15 | - go version
16 | - go env
17 | # pre-installed MinGW at C:\MinGW is 32bit only
18 | # but MSYS2 at C:\msys64 has mingw64
19 | - set PATH=C:\msys64\mingw64\bin;%PATH%
20 | - gcc --version
21 | - g++ --version
22 |
23 | build_script:
24 | - go install -v ./...
25 |
26 | test_script:
27 | - set PATH=C:\gopath\bin;%PATH%
28 | - go test -v ./...
29 |
30 | #artifacts:
31 | # - path: '%GOPATH%\bin\*.exe'
32 | deploy: off
33 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/bench_test.go:
--------------------------------------------------------------------------------
1 | // +build go1.7
2 |
3 | package errors
4 |
5 | import (
6 | "fmt"
7 | "testing"
8 |
9 | stderrors "errors"
10 | )
11 |
12 | func noErrors(at, depth int) error {
13 | if at >= depth {
14 | return stderrors.New("no error")
15 | }
16 | return noErrors(at+1, depth)
17 | }
18 | func yesErrors(at, depth int) error {
19 | if at >= depth {
20 | return New("ye error")
21 | }
22 | return yesErrors(at+1, depth)
23 | }
24 |
25 | func BenchmarkErrors(b *testing.B) {
26 | var toperr error
27 | type run struct {
28 | stack int
29 | std bool
30 | }
31 | runs := []run{
32 | {10, false},
33 | {10, true},
34 | {100, false},
35 | {100, true},
36 | {1000, false},
37 | {1000, true},
38 | }
39 | for _, r := range runs {
40 | part := "pkg/errors"
41 | if r.std {
42 | part = "errors"
43 | }
44 | name := fmt.Sprintf("%s-stack-%d", part, r.stack)
45 | b.Run(name, func(b *testing.B) {
46 | var err error
47 | f := yesErrors
48 | if r.std {
49 | f = noErrors
50 | }
51 | b.ReportAllocs()
52 | for i := 0; i < b.N; i++ {
53 | err = f(0, r.stack)
54 | }
55 | b.StopTimer()
56 | toperr = err
57 | })
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/errors.go:
--------------------------------------------------------------------------------
1 | // Package errors provides simple error handling primitives.
2 | //
3 | // The traditional error handling idiom in Go is roughly akin to
4 | //
5 | // if err != nil {
6 | // return err
7 | // }
8 | //
9 | // which applied recursively up the call stack results in error reports
10 | // without context or debugging information. The errors package allows
11 | // programmers to add context to the failure path in their code in a way
12 | // that does not destroy the original value of the error.
13 | //
14 | // Adding context to an error
15 | //
16 | // The errors.Wrap function returns a new error that adds context to the
17 | // original error by recording a stack trace at the point Wrap is called,
18 | // and the supplied message. For example
19 | //
20 | // _, err := ioutil.ReadAll(r)
21 | // if err != nil {
22 | // return errors.Wrap(err, "read failed")
23 | // }
24 | //
25 | // If additional control is required the errors.WithStack and errors.WithMessage
26 | // functions destructure errors.Wrap into its component operations of annotating
27 | // an error with a stack trace and an a message, respectively.
28 | //
29 | // Retrieving the cause of an error
30 | //
31 | // Using errors.Wrap constructs a stack of errors, adding context to the
32 | // preceding error. Depending on the nature of the error it may be necessary
33 | // to reverse the operation of errors.Wrap to retrieve the original error
34 | // for inspection. Any error value which implements this interface
35 | //
36 | // type causer interface {
37 | // Cause() error
38 | // }
39 | //
40 | // can be inspected by errors.Cause. errors.Cause will recursively retrieve
41 | // the topmost error which does not implement causer, which is assumed to be
42 | // the original cause. For example:
43 | //
44 | // switch err := errors.Cause(err).(type) {
45 | // case *MyError:
46 | // // handle specifically
47 | // default:
48 | // // unknown error
49 | // }
50 | //
51 | // causer interface is not exported by this package, but is considered a part
52 | // of stable public API.
53 | //
54 | // Formatted printing of errors
55 | //
56 | // All error values returned from this package implement fmt.Formatter and can
57 | // be formatted by the fmt package. The following verbs are supported
58 | //
59 | // %s print the error. If the error has a Cause it will be
60 | // printed recursively
61 | // %v see %s
62 | // %+v extended format. Each Frame of the error's StackTrace will
63 | // be printed in detail.
64 | //
65 | // Retrieving the stack trace of an error or wrapper
66 | //
67 | // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
68 | // invoked. This information can be retrieved with the following interface.
69 | //
70 | // type stackTracer interface {
71 | // StackTrace() errors.StackTrace
72 | // }
73 | //
74 | // Where errors.StackTrace is defined as
75 | //
76 | // type StackTrace []Frame
77 | //
78 | // The Frame type represents a call site in the stack trace. Frame supports
79 | // the fmt.Formatter interface that can be used for printing information about
80 | // the stack trace of this error. For example:
81 | //
82 | // if err, ok := err.(stackTracer); ok {
83 | // for _, f := range err.StackTrace() {
84 | // fmt.Printf("%+s:%d", f)
85 | // }
86 | // }
87 | //
88 | // stackTracer interface is not exported by this package, but is considered a part
89 | // of stable public API.
90 | //
91 | // See the documentation for Frame.Format for more details.
92 | package errors
93 |
94 | import (
95 | "fmt"
96 | "io"
97 | )
98 |
99 | // New returns an error with the supplied message.
100 | // New also records the stack trace at the point it was called.
101 | func New(message string) error {
102 | return &fundamental{
103 | msg: message,
104 | stack: callers(),
105 | }
106 | }
107 |
108 | // Errorf formats according to a format specifier and returns the string
109 | // as a value that satisfies error.
110 | // Errorf also records the stack trace at the point it was called.
111 | func Errorf(format string, args ...interface{}) error {
112 | return &fundamental{
113 | msg: fmt.Sprintf(format, args...),
114 | stack: callers(),
115 | }
116 | }
117 |
118 | // fundamental is an error that has a message and a stack, but no caller.
119 | type fundamental struct {
120 | msg string
121 | *stack
122 | }
123 |
124 | func (f *fundamental) Error() string { return f.msg }
125 |
126 | func (f *fundamental) Format(s fmt.State, verb rune) {
127 | switch verb {
128 | case 'v':
129 | if s.Flag('+') {
130 | io.WriteString(s, f.msg)
131 | f.stack.Format(s, verb)
132 | return
133 | }
134 | fallthrough
135 | case 's':
136 | io.WriteString(s, f.msg)
137 | case 'q':
138 | fmt.Fprintf(s, "%q", f.msg)
139 | }
140 | }
141 |
142 | // WithStack annotates err with a stack trace at the point WithStack was called.
143 | // If err is nil, WithStack returns nil.
144 | func WithStack(err error) error {
145 | if err == nil {
146 | return nil
147 | }
148 | return &withStack{
149 | err,
150 | callers(),
151 | }
152 | }
153 |
154 | type withStack struct {
155 | error
156 | *stack
157 | }
158 |
159 | func (w *withStack) Cause() error { return w.error }
160 |
161 | func (w *withStack) Format(s fmt.State, verb rune) {
162 | switch verb {
163 | case 'v':
164 | if s.Flag('+') {
165 | fmt.Fprintf(s, "%+v", w.Cause())
166 | w.stack.Format(s, verb)
167 | return
168 | }
169 | fallthrough
170 | case 's':
171 | io.WriteString(s, w.Error())
172 | case 'q':
173 | fmt.Fprintf(s, "%q", w.Error())
174 | }
175 | }
176 |
177 | // Wrap returns an error annotating err with a stack trace
178 | // at the point Wrap is called, and the supplied message.
179 | // If err is nil, Wrap returns nil.
180 | func Wrap(err error, message string) error {
181 | if err == nil {
182 | return nil
183 | }
184 | err = &withMessage{
185 | cause: err,
186 | msg: message,
187 | }
188 | return &withStack{
189 | err,
190 | callers(),
191 | }
192 | }
193 |
194 | // Wrapf returns an error annotating err with a stack trace
195 | // at the point Wrapf is call, and the format specifier.
196 | // If err is nil, Wrapf returns nil.
197 | func Wrapf(err error, format string, args ...interface{}) error {
198 | if err == nil {
199 | return nil
200 | }
201 | err = &withMessage{
202 | cause: err,
203 | msg: fmt.Sprintf(format, args...),
204 | }
205 | return &withStack{
206 | err,
207 | callers(),
208 | }
209 | }
210 |
211 | // WithMessage annotates err with a new message.
212 | // If err is nil, WithMessage returns nil.
213 | func WithMessage(err error, message string) error {
214 | if err == nil {
215 | return nil
216 | }
217 | return &withMessage{
218 | cause: err,
219 | msg: message,
220 | }
221 | }
222 |
223 | type withMessage struct {
224 | cause error
225 | msg string
226 | }
227 |
228 | func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
229 | func (w *withMessage) Cause() error { return w.cause }
230 |
231 | func (w *withMessage) Format(s fmt.State, verb rune) {
232 | switch verb {
233 | case 'v':
234 | if s.Flag('+') {
235 | fmt.Fprintf(s, "%+v\n", w.Cause())
236 | io.WriteString(s, w.msg)
237 | return
238 | }
239 | fallthrough
240 | case 's', 'q':
241 | io.WriteString(s, w.Error())
242 | }
243 | }
244 |
245 | // Cause returns the underlying cause of the error, if possible.
246 | // An error value has a cause if it implements the following
247 | // interface:
248 | //
249 | // type causer interface {
250 | // Cause() error
251 | // }
252 | //
253 | // If the error does not implement Cause, the original error will
254 | // be returned. If the error is nil, nil will be returned without further
255 | // investigation.
256 | func Cause(err error) error {
257 | type causer interface {
258 | Cause() error
259 | }
260 |
261 | for err != nil {
262 | cause, ok := err.(causer)
263 | if !ok {
264 | break
265 | }
266 | err = cause.Cause()
267 | }
268 | return err
269 | }
270 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/errors_test.go:
--------------------------------------------------------------------------------
1 | package errors
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "io"
7 | "reflect"
8 | "testing"
9 | )
10 |
11 | func TestNew(t *testing.T) {
12 | tests := []struct {
13 | err string
14 | want error
15 | }{
16 | {"", fmt.Errorf("")},
17 | {"foo", fmt.Errorf("foo")},
18 | {"foo", New("foo")},
19 | {"string with format specifiers: %v", errors.New("string with format specifiers: %v")},
20 | }
21 |
22 | for _, tt := range tests {
23 | got := New(tt.err)
24 | if got.Error() != tt.want.Error() {
25 | t.Errorf("New.Error(): got: %q, want %q", got, tt.want)
26 | }
27 | }
28 | }
29 |
30 | func TestWrapNil(t *testing.T) {
31 | got := Wrap(nil, "no error")
32 | if got != nil {
33 | t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got)
34 | }
35 | }
36 |
37 | func TestWrap(t *testing.T) {
38 | tests := []struct {
39 | err error
40 | message string
41 | want string
42 | }{
43 | {io.EOF, "read error", "read error: EOF"},
44 | {Wrap(io.EOF, "read error"), "client error", "client error: read error: EOF"},
45 | }
46 |
47 | for _, tt := range tests {
48 | got := Wrap(tt.err, tt.message).Error()
49 | if got != tt.want {
50 | t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want)
51 | }
52 | }
53 | }
54 |
55 | type nilError struct{}
56 |
57 | func (nilError) Error() string { return "nil error" }
58 |
59 | func TestCause(t *testing.T) {
60 | x := New("error")
61 | tests := []struct {
62 | err error
63 | want error
64 | }{{
65 | // nil error is nil
66 | err: nil,
67 | want: nil,
68 | }, {
69 | // explicit nil error is nil
70 | err: (error)(nil),
71 | want: nil,
72 | }, {
73 | // typed nil is nil
74 | err: (*nilError)(nil),
75 | want: (*nilError)(nil),
76 | }, {
77 | // uncaused error is unaffected
78 | err: io.EOF,
79 | want: io.EOF,
80 | }, {
81 | // caused error returns cause
82 | err: Wrap(io.EOF, "ignored"),
83 | want: io.EOF,
84 | }, {
85 | err: x, // return from errors.New
86 | want: x,
87 | }, {
88 | WithMessage(nil, "whoops"),
89 | nil,
90 | }, {
91 | WithMessage(io.EOF, "whoops"),
92 | io.EOF,
93 | }, {
94 | WithStack(nil),
95 | nil,
96 | }, {
97 | WithStack(io.EOF),
98 | io.EOF,
99 | }}
100 |
101 | for i, tt := range tests {
102 | got := Cause(tt.err)
103 | if !reflect.DeepEqual(got, tt.want) {
104 | t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want)
105 | }
106 | }
107 | }
108 |
109 | func TestWrapfNil(t *testing.T) {
110 | got := Wrapf(nil, "no error")
111 | if got != nil {
112 | t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got)
113 | }
114 | }
115 |
116 | func TestWrapf(t *testing.T) {
117 | tests := []struct {
118 | err error
119 | message string
120 | want string
121 | }{
122 | {io.EOF, "read error", "read error: EOF"},
123 | {Wrapf(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"},
124 | {Wrapf(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"},
125 | }
126 |
127 | for _, tt := range tests {
128 | got := Wrapf(tt.err, tt.message).Error()
129 | if got != tt.want {
130 | t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want)
131 | }
132 | }
133 | }
134 |
135 | func TestErrorf(t *testing.T) {
136 | tests := []struct {
137 | err error
138 | want string
139 | }{
140 | {Errorf("read error without format specifiers"), "read error without format specifiers"},
141 | {Errorf("read error with %d format specifier", 1), "read error with 1 format specifier"},
142 | }
143 |
144 | for _, tt := range tests {
145 | got := tt.err.Error()
146 | if got != tt.want {
147 | t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want)
148 | }
149 | }
150 | }
151 |
152 | func TestWithStackNil(t *testing.T) {
153 | got := WithStack(nil)
154 | if got != nil {
155 | t.Errorf("WithStack(nil): got %#v, expected nil", got)
156 | }
157 | }
158 |
159 | func TestWithStack(t *testing.T) {
160 | tests := []struct {
161 | err error
162 | want string
163 | }{
164 | {io.EOF, "EOF"},
165 | {WithStack(io.EOF), "EOF"},
166 | }
167 |
168 | for _, tt := range tests {
169 | got := WithStack(tt.err).Error()
170 | if got != tt.want {
171 | t.Errorf("WithStack(%v): got: %v, want %v", tt.err, got, tt.want)
172 | }
173 | }
174 | }
175 |
176 | func TestWithMessageNil(t *testing.T) {
177 | got := WithMessage(nil, "no error")
178 | if got != nil {
179 | t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got)
180 | }
181 | }
182 |
183 | func TestWithMessage(t *testing.T) {
184 | tests := []struct {
185 | err error
186 | message string
187 | want string
188 | }{
189 | {io.EOF, "read error", "read error: EOF"},
190 | {WithMessage(io.EOF, "read error"), "client error", "client error: read error: EOF"},
191 | }
192 |
193 | for _, tt := range tests {
194 | got := WithMessage(tt.err, tt.message).Error()
195 | if got != tt.want {
196 | t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want)
197 | }
198 | }
199 |
200 | }
201 |
202 | // errors.New, etc values are not expected to be compared by value
203 | // but the change in errors#27 made them incomparable. Assert that
204 | // various kinds of errors have a functional equality operator, even
205 | // if the result of that equality is always false.
206 | func TestErrorEquality(t *testing.T) {
207 | vals := []error{
208 | nil,
209 | io.EOF,
210 | errors.New("EOF"),
211 | New("EOF"),
212 | Errorf("EOF"),
213 | Wrap(io.EOF, "EOF"),
214 | Wrapf(io.EOF, "EOF%d", 2),
215 | WithMessage(nil, "whoops"),
216 | WithMessage(io.EOF, "whoops"),
217 | WithStack(io.EOF),
218 | WithStack(nil),
219 | }
220 |
221 | for i := range vals {
222 | for j := range vals {
223 | _ = vals[i] == vals[j] // mustn't panic
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/example_test.go:
--------------------------------------------------------------------------------
1 | package errors_test
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/pkg/errors"
7 | )
8 |
9 | func ExampleNew() {
10 | err := errors.New("whoops")
11 | fmt.Println(err)
12 |
13 | // Output: whoops
14 | }
15 |
16 | func ExampleNew_printf() {
17 | err := errors.New("whoops")
18 | fmt.Printf("%+v", err)
19 |
20 | // Example output:
21 | // whoops
22 | // github.com/pkg/errors_test.ExampleNew_printf
23 | // /home/dfc/src/github.com/pkg/errors/example_test.go:17
24 | // testing.runExample
25 | // /home/dfc/go/src/testing/example.go:114
26 | // testing.RunExamples
27 | // /home/dfc/go/src/testing/example.go:38
28 | // testing.(*M).Run
29 | // /home/dfc/go/src/testing/testing.go:744
30 | // main.main
31 | // /github.com/pkg/errors/_test/_testmain.go:106
32 | // runtime.main
33 | // /home/dfc/go/src/runtime/proc.go:183
34 | // runtime.goexit
35 | // /home/dfc/go/src/runtime/asm_amd64.s:2059
36 | }
37 |
38 | func ExampleWithMessage() {
39 | cause := errors.New("whoops")
40 | err := errors.WithMessage(cause, "oh noes")
41 | fmt.Println(err)
42 |
43 | // Output: oh noes: whoops
44 | }
45 |
46 | func ExampleWithStack() {
47 | cause := errors.New("whoops")
48 | err := errors.WithStack(cause)
49 | fmt.Println(err)
50 |
51 | // Output: whoops
52 | }
53 |
54 | func ExampleWithStack_printf() {
55 | cause := errors.New("whoops")
56 | err := errors.WithStack(cause)
57 | fmt.Printf("%+v", err)
58 |
59 | // Example Output:
60 | // whoops
61 | // github.com/pkg/errors_test.ExampleWithStack_printf
62 | // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:55
63 | // testing.runExample
64 | // /usr/lib/go/src/testing/example.go:114
65 | // testing.RunExamples
66 | // /usr/lib/go/src/testing/example.go:38
67 | // testing.(*M).Run
68 | // /usr/lib/go/src/testing/testing.go:744
69 | // main.main
70 | // github.com/pkg/errors/_test/_testmain.go:106
71 | // runtime.main
72 | // /usr/lib/go/src/runtime/proc.go:183
73 | // runtime.goexit
74 | // /usr/lib/go/src/runtime/asm_amd64.s:2086
75 | // github.com/pkg/errors_test.ExampleWithStack_printf
76 | // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:56
77 | // testing.runExample
78 | // /usr/lib/go/src/testing/example.go:114
79 | // testing.RunExamples
80 | // /usr/lib/go/src/testing/example.go:38
81 | // testing.(*M).Run
82 | // /usr/lib/go/src/testing/testing.go:744
83 | // main.main
84 | // github.com/pkg/errors/_test/_testmain.go:106
85 | // runtime.main
86 | // /usr/lib/go/src/runtime/proc.go:183
87 | // runtime.goexit
88 | // /usr/lib/go/src/runtime/asm_amd64.s:2086
89 | }
90 |
91 | func ExampleWrap() {
92 | cause := errors.New("whoops")
93 | err := errors.Wrap(cause, "oh noes")
94 | fmt.Println(err)
95 |
96 | // Output: oh noes: whoops
97 | }
98 |
99 | func fn() error {
100 | e1 := errors.New("error")
101 | e2 := errors.Wrap(e1, "inner")
102 | e3 := errors.Wrap(e2, "middle")
103 | return errors.Wrap(e3, "outer")
104 | }
105 |
106 | func ExampleCause() {
107 | err := fn()
108 | fmt.Println(err)
109 | fmt.Println(errors.Cause(err))
110 |
111 | // Output: outer: middle: inner: error
112 | // error
113 | }
114 |
115 | func ExampleWrap_extended() {
116 | err := fn()
117 | fmt.Printf("%+v\n", err)
118 |
119 | // Example output:
120 | // error
121 | // github.com/pkg/errors_test.fn
122 | // /home/dfc/src/github.com/pkg/errors/example_test.go:47
123 | // github.com/pkg/errors_test.ExampleCause_printf
124 | // /home/dfc/src/github.com/pkg/errors/example_test.go:63
125 | // testing.runExample
126 | // /home/dfc/go/src/testing/example.go:114
127 | // testing.RunExamples
128 | // /home/dfc/go/src/testing/example.go:38
129 | // testing.(*M).Run
130 | // /home/dfc/go/src/testing/testing.go:744
131 | // main.main
132 | // /github.com/pkg/errors/_test/_testmain.go:104
133 | // runtime.main
134 | // /home/dfc/go/src/runtime/proc.go:183
135 | // runtime.goexit
136 | // /home/dfc/go/src/runtime/asm_amd64.s:2059
137 | // github.com/pkg/errors_test.fn
138 | // /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner
139 | // github.com/pkg/errors_test.fn
140 | // /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle
141 | // github.com/pkg/errors_test.fn
142 | // /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer
143 | }
144 |
145 | func ExampleWrapf() {
146 | cause := errors.New("whoops")
147 | err := errors.Wrapf(cause, "oh noes #%d", 2)
148 | fmt.Println(err)
149 |
150 | // Output: oh noes #2: whoops
151 | }
152 |
153 | func ExampleErrorf_extended() {
154 | err := errors.Errorf("whoops: %s", "foo")
155 | fmt.Printf("%+v", err)
156 |
157 | // Example output:
158 | // whoops: foo
159 | // github.com/pkg/errors_test.ExampleErrorf
160 | // /home/dfc/src/github.com/pkg/errors/example_test.go:101
161 | // testing.runExample
162 | // /home/dfc/go/src/testing/example.go:114
163 | // testing.RunExamples
164 | // /home/dfc/go/src/testing/example.go:38
165 | // testing.(*M).Run
166 | // /home/dfc/go/src/testing/testing.go:744
167 | // main.main
168 | // /github.com/pkg/errors/_test/_testmain.go:102
169 | // runtime.main
170 | // /home/dfc/go/src/runtime/proc.go:183
171 | // runtime.goexit
172 | // /home/dfc/go/src/runtime/asm_amd64.s:2059
173 | }
174 |
175 | func Example_stackTrace() {
176 | type stackTracer interface {
177 | StackTrace() errors.StackTrace
178 | }
179 |
180 | err, ok := errors.Cause(fn()).(stackTracer)
181 | if !ok {
182 | panic("oops, err does not implement stackTracer")
183 | }
184 |
185 | st := err.StackTrace()
186 | fmt.Printf("%+v", st[0:2]) // top two frames
187 |
188 | // Example output:
189 | // github.com/pkg/errors_test.fn
190 | // /home/dfc/src/github.com/pkg/errors/example_test.go:47
191 | // github.com/pkg/errors_test.Example_stackTrace
192 | // /home/dfc/src/github.com/pkg/errors/example_test.go:127
193 | }
194 |
195 | func ExampleCause_printf() {
196 | err := errors.Wrap(func() error {
197 | return func() error {
198 | return errors.Errorf("hello %s", fmt.Sprintf("world"))
199 | }()
200 | }(), "failed")
201 |
202 | fmt.Printf("%v", err)
203 |
204 | // Output: failed: hello world
205 | }
206 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/stack.go:
--------------------------------------------------------------------------------
1 | package errors
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "path"
7 | "runtime"
8 | "strings"
9 | )
10 |
11 | // Frame represents a program counter inside a stack frame.
12 | type Frame uintptr
13 |
14 | // pc returns the program counter for this frame;
15 | // multiple frames may have the same PC value.
16 | func (f Frame) pc() uintptr { return uintptr(f) - 1 }
17 |
18 | // file returns the full path to the file that contains the
19 | // function for this Frame's pc.
20 | func (f Frame) file() string {
21 | fn := runtime.FuncForPC(f.pc())
22 | if fn == nil {
23 | return "unknown"
24 | }
25 | file, _ := fn.FileLine(f.pc())
26 | return file
27 | }
28 |
29 | // line returns the line number of source code of the
30 | // function for this Frame's pc.
31 | func (f Frame) line() int {
32 | fn := runtime.FuncForPC(f.pc())
33 | if fn == nil {
34 | return 0
35 | }
36 | _, line := fn.FileLine(f.pc())
37 | return line
38 | }
39 |
40 | // Format formats the frame according to the fmt.Formatter interface.
41 | //
42 | // %s source file
43 | // %d source line
44 | // %n function name
45 | // %v equivalent to %s:%d
46 | //
47 | // Format accepts flags that alter the printing of some verbs, as follows:
48 | //
49 | // %+s path of source file relative to the compile time GOPATH
50 | // %+v equivalent to %+s:%d
51 | func (f Frame) Format(s fmt.State, verb rune) {
52 | switch verb {
53 | case 's':
54 | switch {
55 | case s.Flag('+'):
56 | pc := f.pc()
57 | fn := runtime.FuncForPC(pc)
58 | if fn == nil {
59 | io.WriteString(s, "unknown")
60 | } else {
61 | file, _ := fn.FileLine(pc)
62 | fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
63 | }
64 | default:
65 | io.WriteString(s, path.Base(f.file()))
66 | }
67 | case 'd':
68 | fmt.Fprintf(s, "%d", f.line())
69 | case 'n':
70 | name := runtime.FuncForPC(f.pc()).Name()
71 | io.WriteString(s, funcname(name))
72 | case 'v':
73 | f.Format(s, 's')
74 | io.WriteString(s, ":")
75 | f.Format(s, 'd')
76 | }
77 | }
78 |
79 | // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
80 | type StackTrace []Frame
81 |
82 | func (st StackTrace) Format(s fmt.State, verb rune) {
83 | switch verb {
84 | case 'v':
85 | switch {
86 | case s.Flag('+'):
87 | for _, f := range st {
88 | fmt.Fprintf(s, "\n%+v", f)
89 | }
90 | case s.Flag('#'):
91 | fmt.Fprintf(s, "%#v", []Frame(st))
92 | default:
93 | fmt.Fprintf(s, "%v", []Frame(st))
94 | }
95 | case 's':
96 | fmt.Fprintf(s, "%s", []Frame(st))
97 | }
98 | }
99 |
100 | // stack represents a stack of program counters.
101 | type stack []uintptr
102 |
103 | func (s *stack) Format(st fmt.State, verb rune) {
104 | switch verb {
105 | case 'v':
106 | switch {
107 | case st.Flag('+'):
108 | for _, pc := range *s {
109 | f := Frame(pc)
110 | fmt.Fprintf(st, "\n%+v", f)
111 | }
112 | }
113 | }
114 | }
115 |
116 | func (s *stack) StackTrace() StackTrace {
117 | f := make([]Frame, len(*s))
118 | for i := 0; i < len(f); i++ {
119 | f[i] = Frame((*s)[i])
120 | }
121 | return f
122 | }
123 |
124 | func callers() *stack {
125 | const depth = 32
126 | var pcs [depth]uintptr
127 | n := runtime.Callers(3, pcs[:])
128 | var st stack = pcs[0:n]
129 | return &st
130 | }
131 |
132 | // funcname removes the path prefix component of a function's name reported by func.Name().
133 | func funcname(name string) string {
134 | i := strings.LastIndex(name, "/")
135 | name = name[i+1:]
136 | i = strings.Index(name, ".")
137 | return name[i+1:]
138 | }
139 |
140 | func trimGOPATH(name, file string) string {
141 | // Here we want to get the source file path relative to the compile time
142 | // GOPATH. As of Go 1.6.x there is no direct way to know the compiled
143 | // GOPATH at runtime, but we can infer the number of path segments in the
144 | // GOPATH. We note that fn.Name() returns the function name qualified by
145 | // the import path, which does not include the GOPATH. Thus we can trim
146 | // segments from the beginning of the file path until the number of path
147 | // separators remaining is one more than the number of path separators in
148 | // the function name. For example, given:
149 | //
150 | // GOPATH /home/user
151 | // file /home/user/src/pkg/sub/file.go
152 | // fn.Name() pkg/sub.Type.Method
153 | //
154 | // We want to produce:
155 | //
156 | // pkg/sub/file.go
157 | //
158 | // From this we can easily see that fn.Name() has one less path separator
159 | // than our desired output. We count separators from the end of the file
160 | // path until it finds two more than in the function name and then move
161 | // one character forward to preserve the initial path segment without a
162 | // leading separator.
163 | const sep = "/"
164 | goal := strings.Count(name, sep) + 2
165 | i := len(file)
166 | for n := 0; n < goal; n++ {
167 | i = strings.LastIndex(file[:i], sep)
168 | if i == -1 {
169 | // not enough separators found, set i so that the slice expression
170 | // below leaves file unmodified
171 | i = -len(sep)
172 | break
173 | }
174 | }
175 | // get back to 0 or trim the leading separator
176 | file = file[i+len(sep):]
177 | return file
178 | }
179 |
--------------------------------------------------------------------------------
/vendor/github.com/pkg/errors/stack_test.go:
--------------------------------------------------------------------------------
1 | package errors
2 |
3 | import (
4 | "fmt"
5 | "runtime"
6 | "testing"
7 | )
8 |
9 | var initpc, _, _, _ = runtime.Caller(0)
10 |
11 | func TestFrameLine(t *testing.T) {
12 | var tests = []struct {
13 | Frame
14 | want int
15 | }{{
16 | Frame(initpc),
17 | 9,
18 | }, {
19 | func() Frame {
20 | var pc, _, _, _ = runtime.Caller(0)
21 | return Frame(pc)
22 | }(),
23 | 20,
24 | }, {
25 | func() Frame {
26 | var pc, _, _, _ = runtime.Caller(1)
27 | return Frame(pc)
28 | }(),
29 | 28,
30 | }, {
31 | Frame(0), // invalid PC
32 | 0,
33 | }}
34 |
35 | for _, tt := range tests {
36 | got := tt.Frame.line()
37 | want := tt.want
38 | if want != got {
39 | t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got)
40 | }
41 | }
42 | }
43 |
44 | type X struct{}
45 |
46 | func (x X) val() Frame {
47 | var pc, _, _, _ = runtime.Caller(0)
48 | return Frame(pc)
49 | }
50 |
51 | func (x *X) ptr() Frame {
52 | var pc, _, _, _ = runtime.Caller(0)
53 | return Frame(pc)
54 | }
55 |
56 | func TestFrameFormat(t *testing.T) {
57 | var tests = []struct {
58 | Frame
59 | format string
60 | want string
61 | }{{
62 | Frame(initpc),
63 | "%s",
64 | "stack_test.go",
65 | }, {
66 | Frame(initpc),
67 | "%+s",
68 | "github.com/pkg/errors.init\n" +
69 | "\t.+/github.com/pkg/errors/stack_test.go",
70 | }, {
71 | Frame(0),
72 | "%s",
73 | "unknown",
74 | }, {
75 | Frame(0),
76 | "%+s",
77 | "unknown",
78 | }, {
79 | Frame(initpc),
80 | "%d",
81 | "9",
82 | }, {
83 | Frame(0),
84 | "%d",
85 | "0",
86 | }, {
87 | Frame(initpc),
88 | "%n",
89 | "init",
90 | }, {
91 | func() Frame {
92 | var x X
93 | return x.ptr()
94 | }(),
95 | "%n",
96 | `\(\*X\).ptr`,
97 | }, {
98 | func() Frame {
99 | var x X
100 | return x.val()
101 | }(),
102 | "%n",
103 | "X.val",
104 | }, {
105 | Frame(0),
106 | "%n",
107 | "",
108 | }, {
109 | Frame(initpc),
110 | "%v",
111 | "stack_test.go:9",
112 | }, {
113 | Frame(initpc),
114 | "%+v",
115 | "github.com/pkg/errors.init\n" +
116 | "\t.+/github.com/pkg/errors/stack_test.go:9",
117 | }, {
118 | Frame(0),
119 | "%v",
120 | "unknown:0",
121 | }}
122 |
123 | for i, tt := range tests {
124 | testFormatRegexp(t, i, tt.Frame, tt.format, tt.want)
125 | }
126 | }
127 |
128 | func TestFuncname(t *testing.T) {
129 | tests := []struct {
130 | name, want string
131 | }{
132 | {"", ""},
133 | {"runtime.main", "main"},
134 | {"github.com/pkg/errors.funcname", "funcname"},
135 | {"funcname", "funcname"},
136 | {"io.copyBuffer", "copyBuffer"},
137 | {"main.(*R).Write", "(*R).Write"},
138 | }
139 |
140 | for _, tt := range tests {
141 | got := funcname(tt.name)
142 | want := tt.want
143 | if got != want {
144 | t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got)
145 | }
146 | }
147 | }
148 |
149 | func TestTrimGOPATH(t *testing.T) {
150 | var tests = []struct {
151 | Frame
152 | want string
153 | }{{
154 | Frame(initpc),
155 | "github.com/pkg/errors/stack_test.go",
156 | }}
157 |
158 | for i, tt := range tests {
159 | pc := tt.Frame.pc()
160 | fn := runtime.FuncForPC(pc)
161 | file, _ := fn.FileLine(pc)
162 | got := trimGOPATH(fn.Name(), file)
163 | testFormatRegexp(t, i, got, "%s", tt.want)
164 | }
165 | }
166 |
167 | func TestStackTrace(t *testing.T) {
168 | tests := []struct {
169 | err error
170 | want []string
171 | }{{
172 | New("ooh"), []string{
173 | "github.com/pkg/errors.TestStackTrace\n" +
174 | "\t.+/github.com/pkg/errors/stack_test.go:172",
175 | },
176 | }, {
177 | Wrap(New("ooh"), "ahh"), []string{
178 | "github.com/pkg/errors.TestStackTrace\n" +
179 | "\t.+/github.com/pkg/errors/stack_test.go:177", // this is the stack of Wrap, not New
180 | },
181 | }, {
182 | Cause(Wrap(New("ooh"), "ahh")), []string{
183 | "github.com/pkg/errors.TestStackTrace\n" +
184 | "\t.+/github.com/pkg/errors/stack_test.go:182", // this is the stack of New
185 | },
186 | }, {
187 | func() error { return New("ooh") }(), []string{
188 | `github.com/pkg/errors.(func·009|TestStackTrace.func1)` +
189 | "\n\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New
190 | "github.com/pkg/errors.TestStackTrace\n" +
191 | "\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New's caller
192 | },
193 | }, {
194 | Cause(func() error {
195 | return func() error {
196 | return Errorf("hello %s", fmt.Sprintf("world"))
197 | }()
198 | }()), []string{
199 | `github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` +
200 | "\n\t.+/github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf
201 | `github.com/pkg/errors.(func·011|TestStackTrace.func2)` +
202 | "\n\t.+/github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller
203 | "github.com/pkg/errors.TestStackTrace\n" +
204 | "\t.+/github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller
205 | },
206 | }}
207 | for i, tt := range tests {
208 | x, ok := tt.err.(interface {
209 | StackTrace() StackTrace
210 | })
211 | if !ok {
212 | t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err)
213 | continue
214 | }
215 | st := x.StackTrace()
216 | for j, want := range tt.want {
217 | testFormatRegexp(t, i, st[j], "%+v", want)
218 | }
219 | }
220 | }
221 |
222 | func stackTrace() StackTrace {
223 | const depth = 8
224 | var pcs [depth]uintptr
225 | n := runtime.Callers(1, pcs[:])
226 | var st stack = pcs[0:n]
227 | return st.StackTrace()
228 | }
229 |
230 | func TestStackTraceFormat(t *testing.T) {
231 | tests := []struct {
232 | StackTrace
233 | format string
234 | want string
235 | }{{
236 | nil,
237 | "%s",
238 | `\[\]`,
239 | }, {
240 | nil,
241 | "%v",
242 | `\[\]`,
243 | }, {
244 | nil,
245 | "%+v",
246 | "",
247 | }, {
248 | nil,
249 | "%#v",
250 | `\[\]errors.Frame\(nil\)`,
251 | }, {
252 | make(StackTrace, 0),
253 | "%s",
254 | `\[\]`,
255 | }, {
256 | make(StackTrace, 0),
257 | "%v",
258 | `\[\]`,
259 | }, {
260 | make(StackTrace, 0),
261 | "%+v",
262 | "",
263 | }, {
264 | make(StackTrace, 0),
265 | "%#v",
266 | `\[\]errors.Frame{}`,
267 | }, {
268 | stackTrace()[:2],
269 | "%s",
270 | `\[stack_test.go stack_test.go\]`,
271 | }, {
272 | stackTrace()[:2],
273 | "%v",
274 | `\[stack_test.go:225 stack_test.go:272\]`,
275 | }, {
276 | stackTrace()[:2],
277 | "%+v",
278 | "\n" +
279 | "github.com/pkg/errors.stackTrace\n" +
280 | "\t.+/github.com/pkg/errors/stack_test.go:225\n" +
281 | "github.com/pkg/errors.TestStackTraceFormat\n" +
282 | "\t.+/github.com/pkg/errors/stack_test.go:276",
283 | }, {
284 | stackTrace()[:2],
285 | "%#v",
286 | `\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`,
287 | }}
288 |
289 | for i, tt := range tests {
290 | testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want)
291 | }
292 | }
293 |
--------------------------------------------------------------------------------