@@ -1,3 +0,0 @@
 | 
			
		||||
language: go
 | 
			
		||||
go:
 | 
			
		||||
  - "1.15"
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
# REST API 2.0 Discogs.com client
 | 
			
		||||
 | 
			
		||||
[](https://travis-ci.org/irlndts/go-discogs)[](https://goreportcard.com/report/github.com/irlndts/go-discogs)
 | 
			
		||||
 | 
			
		||||
go-discogs is a Go client library for the [Discogs API](https://www.discogs.com/developers/). Check the usage section to see how to access the Discogs API.
 | 
			
		||||
 | 
			
		||||
The lib is under MIT but be sure you are familiar with [Discogs API Terms of Use](https://support.discogs.com/hc/en-us/articles/360009334593-API-Terms-of-Use).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
module github.com/irlndts/go-discogs
 | 
			
		||||
 | 
			
		||||
go 1.15
 | 
			
		||||
go 1.16
 | 
			
		||||
 | 
			
		||||
require github.com/google/go-cmp v0.4.1
 | 
			
		||||
require github.com/google/go-cmp v0.5.4
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
 | 
			
		||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
 | 
			
		||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										81
									
								
								vendor/github.com/google/go-cmp/cmp/compare.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										81
									
								
								vendor/github.com/google/go-cmp/cmp/compare.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package cmp determines equality of values.
 | 
			
		||||
//
 | 
			
		||||
@@ -90,6 +90,52 @@ import (
 | 
			
		||||
// If there is a cycle, then the pointed at values are considered equal
 | 
			
		||||
// only if both addresses were previously visited in the same path step.
 | 
			
		||||
func Equal(x, y interface{}, opts ...Option) bool {
 | 
			
		||||
	s := newState(opts)
 | 
			
		||||
	s.compareAny(rootStep(x, y))
 | 
			
		||||
	return s.result.Equal()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Diff returns a human-readable report of the differences between two values:
 | 
			
		||||
// y - x. It returns an empty string if and only if Equal returns true for the
 | 
			
		||||
// same input values and options.
 | 
			
		||||
//
 | 
			
		||||
// The output is displayed as a literal in pseudo-Go syntax.
 | 
			
		||||
// At the start of each line, a "-" prefix indicates an element removed from x,
 | 
			
		||||
// a "+" prefix to indicates an element added from y, and the lack of a prefix
 | 
			
		||||
// indicates an element common to both x and y. If possible, the output
 | 
			
		||||
// uses fmt.Stringer.String or error.Error methods to produce more humanly
 | 
			
		||||
// readable outputs. In such cases, the string is prefixed with either an
 | 
			
		||||
// 's' or 'e' character, respectively, to indicate that the method was called.
 | 
			
		||||
//
 | 
			
		||||
// Do not depend on this output being stable. If you need the ability to
 | 
			
		||||
// programmatically interpret the difference, consider using a custom Reporter.
 | 
			
		||||
func Diff(x, y interface{}, opts ...Option) string {
 | 
			
		||||
	s := newState(opts)
 | 
			
		||||
 | 
			
		||||
	// Optimization: If there are no other reporters, we can optimize for the
 | 
			
		||||
	// common case where the result is equal (and thus no reported difference).
 | 
			
		||||
	// This avoids the expensive construction of a difference tree.
 | 
			
		||||
	if len(s.reporters) == 0 {
 | 
			
		||||
		s.compareAny(rootStep(x, y))
 | 
			
		||||
		if s.result.Equal() {
 | 
			
		||||
			return ""
 | 
			
		||||
		}
 | 
			
		||||
		s.result = diff.Result{} // Reset results
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := new(defaultReporter)
 | 
			
		||||
	s.reporters = append(s.reporters, reporter{r})
 | 
			
		||||
	s.compareAny(rootStep(x, y))
 | 
			
		||||
	d := r.String()
 | 
			
		||||
	if (d == "") != s.result.Equal() {
 | 
			
		||||
		panic("inconsistent difference and equality results")
 | 
			
		||||
	}
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rootStep constructs the first path step. If x and y have differing types,
 | 
			
		||||
// then they are stored within an empty interface type.
 | 
			
		||||
func rootStep(x, y interface{}) PathStep {
 | 
			
		||||
	vx := reflect.ValueOf(x)
 | 
			
		||||
	vy := reflect.ValueOf(y)
 | 
			
		||||
 | 
			
		||||
@@ -112,33 +158,7 @@ func Equal(x, y interface{}, opts ...Option) bool {
 | 
			
		||||
		t = vx.Type()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := newState(opts)
 | 
			
		||||
	s.compareAny(&pathStep{t, vx, vy})
 | 
			
		||||
	return s.result.Equal()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Diff returns a human-readable report of the differences between two values.
 | 
			
		||||
// It returns an empty string if and only if Equal returns true for the same
 | 
			
		||||
// input values and options.
 | 
			
		||||
//
 | 
			
		||||
// The output is displayed as a literal in pseudo-Go syntax.
 | 
			
		||||
// At the start of each line, a "-" prefix indicates an element removed from x,
 | 
			
		||||
// a "+" prefix to indicates an element added to y, and the lack of a prefix
 | 
			
		||||
// indicates an element common to both x and y. If possible, the output
 | 
			
		||||
// uses fmt.Stringer.String or error.Error methods to produce more humanly
 | 
			
		||||
// readable outputs. In such cases, the string is prefixed with either an
 | 
			
		||||
// 's' or 'e' character, respectively, to indicate that the method was called.
 | 
			
		||||
//
 | 
			
		||||
// Do not depend on this output being stable. If you need the ability to
 | 
			
		||||
// programmatically interpret the difference, consider using a custom Reporter.
 | 
			
		||||
func Diff(x, y interface{}, opts ...Option) string {
 | 
			
		||||
	r := new(defaultReporter)
 | 
			
		||||
	eq := Equal(x, y, Options(opts), Reporter(r))
 | 
			
		||||
	d := r.String()
 | 
			
		||||
	if (d == "") != eq {
 | 
			
		||||
		panic("inconsistent difference and equality results")
 | 
			
		||||
	}
 | 
			
		||||
	return d
 | 
			
		||||
	return &pathStep{t, vx, vy}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type state struct {
 | 
			
		||||
@@ -356,7 +376,7 @@ func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) {
 | 
			
		||||
// assuming that T is assignable to R.
 | 
			
		||||
// Otherwise, it returns the input value as is.
 | 
			
		||||
func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
 | 
			
		||||
	// TODO(dsnet): Workaround for reflect bug (https://golang.org/issue/22143).
 | 
			
		||||
	// TODO(≥go1.10): Workaround for reflect bug (https://golang.org/issue/22143).
 | 
			
		||||
	if !flags.AtLeastGo110 {
 | 
			
		||||
		if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t {
 | 
			
		||||
			return reflect.New(t).Elem()
 | 
			
		||||
@@ -366,6 +386,7 @@ func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
 | 
			
		||||
	var addr bool
 | 
			
		||||
	var vax, vay reflect.Value // Addressable versions of vx and vy
 | 
			
		||||
 | 
			
		||||
	var mayForce, mayForceInit bool
 | 
			
		||||
@@ -387,6 +408,7 @@ func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
 | 
			
		||||
				// For retrieveUnexportedField to work, the parent struct must
 | 
			
		||||
				// be addressable. Create a new copy of the values if
 | 
			
		||||
				// necessary to make them addressable.
 | 
			
		||||
				addr = vx.CanAddr() || vy.CanAddr()
 | 
			
		||||
				vax = makeAddressable(vx)
 | 
			
		||||
				vay = makeAddressable(vy)
 | 
			
		||||
			}
 | 
			
		||||
@@ -397,6 +419,7 @@ func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
 | 
			
		||||
				mayForceInit = true
 | 
			
		||||
			}
 | 
			
		||||
			step.mayForce = mayForce
 | 
			
		||||
			step.paddr = addr
 | 
			
		||||
			step.pvx = vax
 | 
			
		||||
			step.pvy = vay
 | 
			
		||||
			step.field = t.Field(i)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/google/go-cmp/cmp/export_panic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/google/go-cmp/cmp/export_panic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build purego
 | 
			
		||||
 | 
			
		||||
@@ -10,6 +10,6 @@ import "reflect"
 | 
			
		||||
 | 
			
		||||
const supportExporters = false
 | 
			
		||||
 | 
			
		||||
func retrieveUnexportedField(reflect.Value, reflect.StructField) reflect.Value {
 | 
			
		||||
func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value {
 | 
			
		||||
	panic("no support for forcibly accessing unexported fields")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/google/go-cmp/cmp/export_unsafe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/google/go-cmp/cmp/export_unsafe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !purego
 | 
			
		||||
 | 
			
		||||
@@ -17,9 +17,19 @@ const supportExporters = true
 | 
			
		||||
// a struct such that the value has read-write permissions.
 | 
			
		||||
//
 | 
			
		||||
// The parent struct, v, must be addressable, while f must be a StructField
 | 
			
		||||
// describing the field to retrieve.
 | 
			
		||||
func retrieveUnexportedField(v reflect.Value, f reflect.StructField) reflect.Value {
 | 
			
		||||
	// See https://github.com/google/go-cmp/issues/167 for discussion of the
 | 
			
		||||
	// following expression.
 | 
			
		||||
	return reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem()
 | 
			
		||||
// describing the field to retrieve. If addr is false,
 | 
			
		||||
// then the returned value will be shallowed copied to be non-addressable.
 | 
			
		||||
func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value {
 | 
			
		||||
	ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem()
 | 
			
		||||
	if !addr {
 | 
			
		||||
		// A field is addressable if and only if the struct is addressable.
 | 
			
		||||
		// If the original parent value was not addressable, shallow copy the
 | 
			
		||||
		// value to make it non-addressable to avoid leaking an implementation
 | 
			
		||||
		// detail of how forcibly exporting a field works.
 | 
			
		||||
		if ve.Kind() == reflect.Interface && ve.IsNil() {
 | 
			
		||||
			return reflect.Zero(f.Type)
 | 
			
		||||
		}
 | 
			
		||||
		return reflect.ValueOf(ve.Interface()).Convert(f.Type)
 | 
			
		||||
	}
 | 
			
		||||
	return ve
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !cmp_debug
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build cmp_debug
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										44
									
								
								vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package diff implements an algorithm for producing edit-scripts.
 | 
			
		||||
// The edit-script is a sequence of operations needed to transform one list
 | 
			
		||||
@@ -12,6 +12,13 @@
 | 
			
		||||
// is more important than obtaining a minimal Levenshtein distance.
 | 
			
		||||
package diff
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/go-cmp/cmp/internal/flags"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EditType represents a single operation within an edit-script.
 | 
			
		||||
type EditType uint8
 | 
			
		||||
 | 
			
		||||
@@ -112,6 +119,8 @@ func (r Result) Similar() bool {
 | 
			
		||||
	return r.NumSame+1 >= r.NumDiff
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
 | 
			
		||||
 | 
			
		||||
// Difference reports whether two lists of lengths nx and ny are equal
 | 
			
		||||
// given the definition of equality provided as f.
 | 
			
		||||
//
 | 
			
		||||
@@ -177,6 +186,11 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) {
 | 
			
		||||
	// approximately the square-root of the search budget.
 | 
			
		||||
	searchBudget := 4 * (nx + ny) // O(n)
 | 
			
		||||
 | 
			
		||||
	// Running the tests with the "cmp_debug" build tag prints a visualization
 | 
			
		||||
	// of the algorithm running in real-time. This is educational for
 | 
			
		||||
	// understanding how the algorithm works. See debug_enable.go.
 | 
			
		||||
	f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es)
 | 
			
		||||
 | 
			
		||||
	// The algorithm below is a greedy, meet-in-the-middle algorithm for
 | 
			
		||||
	// computing sub-optimal edit-scripts between two lists.
 | 
			
		||||
	//
 | 
			
		||||
@@ -194,20 +208,26 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) {
 | 
			
		||||
	//	frontier towards the opposite corner.
 | 
			
		||||
	//	• This algorithm terminates when either the X coordinates or the
 | 
			
		||||
	//	Y coordinates of the forward and reverse frontier points ever intersect.
 | 
			
		||||
	//
 | 
			
		||||
 | 
			
		||||
	// This algorithm is correct even if searching only in the forward direction
 | 
			
		||||
	// or in the reverse direction. We do both because it is commonly observed
 | 
			
		||||
	// that two lists commonly differ because elements were added to the front
 | 
			
		||||
	// or end of the other list.
 | 
			
		||||
	//
 | 
			
		||||
	// Running the tests with the "cmp_debug" build tag prints a visualization
 | 
			
		||||
	// of the algorithm running in real-time. This is educational for
 | 
			
		||||
	// understanding how the algorithm works. See debug_enable.go.
 | 
			
		||||
	f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es)
 | 
			
		||||
	for {
 | 
			
		||||
	// Non-deterministically start with either the forward or reverse direction
 | 
			
		||||
	// to introduce some deliberate instability so that we have the flexibility
 | 
			
		||||
	// to change this algorithm in the future.
 | 
			
		||||
	if flags.Deterministic || randBool {
 | 
			
		||||
		goto forwardSearch
 | 
			
		||||
	} else {
 | 
			
		||||
		goto reverseSearch
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
forwardSearch:
 | 
			
		||||
	{
 | 
			
		||||
		// Forward search from the beginning.
 | 
			
		||||
		if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 {
 | 
			
		||||
			break
 | 
			
		||||
			goto finishSearch
 | 
			
		||||
		}
 | 
			
		||||
		for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ {
 | 
			
		||||
			// Search in a diagonal pattern for a match.
 | 
			
		||||
@@ -242,10 +262,14 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) {
 | 
			
		||||
		} else {
 | 
			
		||||
			fwdFrontier.Y++
 | 
			
		||||
		}
 | 
			
		||||
		goto reverseSearch
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
reverseSearch:
 | 
			
		||||
	{
 | 
			
		||||
		// Reverse search from the end.
 | 
			
		||||
		if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 {
 | 
			
		||||
			break
 | 
			
		||||
			goto finishSearch
 | 
			
		||||
		}
 | 
			
		||||
		for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ {
 | 
			
		||||
			// Search in a diagonal pattern for a match.
 | 
			
		||||
@@ -280,8 +304,10 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) {
 | 
			
		||||
		} else {
 | 
			
		||||
			revFrontier.Y--
 | 
			
		||||
		}
 | 
			
		||||
		goto forwardSearch
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
finishSearch:
 | 
			
		||||
	// Join the forward and reverse paths and then append the reverse path.
 | 
			
		||||
	fwdPath.connect(revPath.point, f)
 | 
			
		||||
	for i := len(revPath.es) - 1; i >= 0; i-- {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2019, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package flags
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2019, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !go1.10
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2019, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build go1.10
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/function/func.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/function/func.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package function provides functionality for identifying function types.
 | 
			
		||||
package function
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										157
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/name.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/name.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,157 @@
 | 
			
		||||
// Copyright 2020, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package value
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TypeString is nearly identical to reflect.Type.String,
 | 
			
		||||
// but has an additional option to specify that full type names be used.
 | 
			
		||||
func TypeString(t reflect.Type, qualified bool) string {
 | 
			
		||||
	return string(appendTypeName(nil, t, qualified, false))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte {
 | 
			
		||||
	// BUG: Go reflection provides no way to disambiguate two named types
 | 
			
		||||
	// of the same name and within the same package,
 | 
			
		||||
	// but declared within the namespace of different functions.
 | 
			
		||||
 | 
			
		||||
	// Named type.
 | 
			
		||||
	if t.Name() != "" {
 | 
			
		||||
		if qualified && t.PkgPath() != "" {
 | 
			
		||||
			b = append(b, '"')
 | 
			
		||||
			b = append(b, t.PkgPath()...)
 | 
			
		||||
			b = append(b, '"')
 | 
			
		||||
			b = append(b, '.')
 | 
			
		||||
			b = append(b, t.Name()...)
 | 
			
		||||
		} else {
 | 
			
		||||
			b = append(b, t.String()...)
 | 
			
		||||
		}
 | 
			
		||||
		return b
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Unnamed type.
 | 
			
		||||
	switch k := t.Kind(); k {
 | 
			
		||||
	case reflect.Bool, reflect.String, reflect.UnsafePointer,
 | 
			
		||||
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
 | 
			
		||||
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
 | 
			
		||||
		reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
 | 
			
		||||
		b = append(b, k.String()...)
 | 
			
		||||
	case reflect.Chan:
 | 
			
		||||
		if t.ChanDir() == reflect.RecvDir {
 | 
			
		||||
			b = append(b, "<-"...)
 | 
			
		||||
		}
 | 
			
		||||
		b = append(b, "chan"...)
 | 
			
		||||
		if t.ChanDir() == reflect.SendDir {
 | 
			
		||||
			b = append(b, "<-"...)
 | 
			
		||||
		}
 | 
			
		||||
		b = append(b, ' ')
 | 
			
		||||
		b = appendTypeName(b, t.Elem(), qualified, false)
 | 
			
		||||
	case reflect.Func:
 | 
			
		||||
		if !elideFunc {
 | 
			
		||||
			b = append(b, "func"...)
 | 
			
		||||
		}
 | 
			
		||||
		b = append(b, '(')
 | 
			
		||||
		for i := 0; i < t.NumIn(); i++ {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				b = append(b, ", "...)
 | 
			
		||||
			}
 | 
			
		||||
			if i == t.NumIn()-1 && t.IsVariadic() {
 | 
			
		||||
				b = append(b, "..."...)
 | 
			
		||||
				b = appendTypeName(b, t.In(i).Elem(), qualified, false)
 | 
			
		||||
			} else {
 | 
			
		||||
				b = appendTypeName(b, t.In(i), qualified, false)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		b = append(b, ')')
 | 
			
		||||
		switch t.NumOut() {
 | 
			
		||||
		case 0:
 | 
			
		||||
			// Do nothing
 | 
			
		||||
		case 1:
 | 
			
		||||
			b = append(b, ' ')
 | 
			
		||||
			b = appendTypeName(b, t.Out(0), qualified, false)
 | 
			
		||||
		default:
 | 
			
		||||
			b = append(b, " ("...)
 | 
			
		||||
			for i := 0; i < t.NumOut(); i++ {
 | 
			
		||||
				if i > 0 {
 | 
			
		||||
					b = append(b, ", "...)
 | 
			
		||||
				}
 | 
			
		||||
				b = appendTypeName(b, t.Out(i), qualified, false)
 | 
			
		||||
			}
 | 
			
		||||
			b = append(b, ')')
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		b = append(b, "struct{ "...)
 | 
			
		||||
		for i := 0; i < t.NumField(); i++ {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				b = append(b, "; "...)
 | 
			
		||||
			}
 | 
			
		||||
			sf := t.Field(i)
 | 
			
		||||
			if !sf.Anonymous {
 | 
			
		||||
				if qualified && sf.PkgPath != "" {
 | 
			
		||||
					b = append(b, '"')
 | 
			
		||||
					b = append(b, sf.PkgPath...)
 | 
			
		||||
					b = append(b, '"')
 | 
			
		||||
					b = append(b, '.')
 | 
			
		||||
				}
 | 
			
		||||
				b = append(b, sf.Name...)
 | 
			
		||||
				b = append(b, ' ')
 | 
			
		||||
			}
 | 
			
		||||
			b = appendTypeName(b, sf.Type, qualified, false)
 | 
			
		||||
			if sf.Tag != "" {
 | 
			
		||||
				b = append(b, ' ')
 | 
			
		||||
				b = strconv.AppendQuote(b, string(sf.Tag))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if b[len(b)-1] == ' ' {
 | 
			
		||||
			b = b[:len(b)-1]
 | 
			
		||||
		} else {
 | 
			
		||||
			b = append(b, ' ')
 | 
			
		||||
		}
 | 
			
		||||
		b = append(b, '}')
 | 
			
		||||
	case reflect.Slice, reflect.Array:
 | 
			
		||||
		b = append(b, '[')
 | 
			
		||||
		if k == reflect.Array {
 | 
			
		||||
			b = strconv.AppendUint(b, uint64(t.Len()), 10)
 | 
			
		||||
		}
 | 
			
		||||
		b = append(b, ']')
 | 
			
		||||
		b = appendTypeName(b, t.Elem(), qualified, false)
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		b = append(b, "map["...)
 | 
			
		||||
		b = appendTypeName(b, t.Key(), qualified, false)
 | 
			
		||||
		b = append(b, ']')
 | 
			
		||||
		b = appendTypeName(b, t.Elem(), qualified, false)
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		b = append(b, '*')
 | 
			
		||||
		b = appendTypeName(b, t.Elem(), qualified, false)
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		b = append(b, "interface{ "...)
 | 
			
		||||
		for i := 0; i < t.NumMethod(); i++ {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				b = append(b, "; "...)
 | 
			
		||||
			}
 | 
			
		||||
			m := t.Method(i)
 | 
			
		||||
			if qualified && m.PkgPath != "" {
 | 
			
		||||
				b = append(b, '"')
 | 
			
		||||
				b = append(b, m.PkgPath...)
 | 
			
		||||
				b = append(b, '"')
 | 
			
		||||
				b = append(b, '.')
 | 
			
		||||
			}
 | 
			
		||||
			b = append(b, m.Name...)
 | 
			
		||||
			b = appendTypeName(b, m.Type, qualified, true)
 | 
			
		||||
		}
 | 
			
		||||
		if b[len(b)-1] == ' ' {
 | 
			
		||||
			b = b[:len(b)-1]
 | 
			
		||||
		} else {
 | 
			
		||||
			b = append(b, ' ')
 | 
			
		||||
		}
 | 
			
		||||
		b = append(b, '}')
 | 
			
		||||
	default:
 | 
			
		||||
		panic("invalid kind: " + k.String())
 | 
			
		||||
	}
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2018, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build purego
 | 
			
		||||
 | 
			
		||||
@@ -21,3 +21,13 @@ func PointerOf(v reflect.Value) Pointer {
 | 
			
		||||
	// assumes that the GC implementation does not use a moving collector.
 | 
			
		||||
	return Pointer{v.Pointer(), v.Type()}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsNil reports whether the pointer is nil.
 | 
			
		||||
func (p Pointer) IsNil() bool {
 | 
			
		||||
	return p.p == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Uintptr returns the pointer as a uintptr.
 | 
			
		||||
func (p Pointer) Uintptr() uintptr {
 | 
			
		||||
	return p.p
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2018, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !purego
 | 
			
		||||
 | 
			
		||||
@@ -24,3 +24,13 @@ func PointerOf(v reflect.Value) Pointer {
 | 
			
		||||
	// which is necessary if the GC ever uses a moving collector.
 | 
			
		||||
	return Pointer{unsafe.Pointer(v.Pointer()), v.Type()}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsNil reports whether the pointer is nil.
 | 
			
		||||
func (p Pointer) IsNil() bool {
 | 
			
		||||
	return p.p == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Uintptr returns the pointer as a uintptr.
 | 
			
		||||
func (p Pointer) Uintptr() uintptr {
 | 
			
		||||
	return uintptr(p.p)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/sort.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/sort.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package value
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/zero.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/internal/value/zero.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package value
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/github.com/google/go-cmp/cmp/options.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/google/go-cmp/cmp/options.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
@@ -225,11 +225,14 @@ func (validator) apply(s *state, vx, vy reflect.Value) {
 | 
			
		||||
 | 
			
		||||
	// Unable to Interface implies unexported field without visibility access.
 | 
			
		||||
	if !vx.CanInterface() || !vy.CanInterface() {
 | 
			
		||||
		const help = "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported"
 | 
			
		||||
		help := "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported"
 | 
			
		||||
		var name string
 | 
			
		||||
		if t := s.curPath.Index(-2).Type(); t.Name() != "" {
 | 
			
		||||
			// Named type with unexported fields.
 | 
			
		||||
			name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType
 | 
			
		||||
			if _, ok := reflect.New(t).Interface().(error); ok {
 | 
			
		||||
				help = "consider using cmpopts.EquateErrors to compare error values"
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Unnamed type with unexported fields. Derive PkgPath from field.
 | 
			
		||||
			var pkgPath string
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								vendor/github.com/google/go-cmp/cmp/path.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/google/go-cmp/cmp/path.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
@@ -177,7 +177,8 @@ type structField struct {
 | 
			
		||||
	// pvx, pvy, and field are only valid if unexported is true.
 | 
			
		||||
	unexported bool
 | 
			
		||||
	mayForce   bool                // Forcibly allow visibility
 | 
			
		||||
	pvx, pvy   reflect.Value       // Parent values
 | 
			
		||||
	paddr      bool                // Was parent addressable?
 | 
			
		||||
	pvx, pvy   reflect.Value       // Parent values (always addressible)
 | 
			
		||||
	field      reflect.StructField // Field information
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -189,8 +190,8 @@ func (sf StructField) Values() (vx, vy reflect.Value) {
 | 
			
		||||
 | 
			
		||||
	// Forcibly obtain read-write access to an unexported struct field.
 | 
			
		||||
	if sf.mayForce {
 | 
			
		||||
		vx = retrieveUnexportedField(sf.pvx, sf.field)
 | 
			
		||||
		vy = retrieveUnexportedField(sf.pvy, sf.field)
 | 
			
		||||
		vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr)
 | 
			
		||||
		vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr)
 | 
			
		||||
		return vx, vy // CanInterface reports true
 | 
			
		||||
	}
 | 
			
		||||
	return sf.vx, sf.vy // CanInterface reports false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/github.com/google/go-cmp/cmp/report.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/google/go-cmp/cmp/report.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +41,10 @@ func (r *defaultReporter) String() string {
 | 
			
		||||
	if r.root.NumDiff == 0 {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return formatOptions{}.FormatDiff(r.root).String()
 | 
			
		||||
	ptrs := new(pointerReferences)
 | 
			
		||||
	text := formatOptions{}.FormatDiff(r.root, ptrs)
 | 
			
		||||
	resolveReferences(text)
 | 
			
		||||
	return text.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func assert(ok bool) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										201
									
								
								vendor/github.com/google/go-cmp/cmp/report_compare.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/google/go-cmp/cmp/report_compare.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2019, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
@@ -11,14 +11,6 @@ import (
 | 
			
		||||
	"github.com/google/go-cmp/cmp/internal/value"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TODO: Enforce limits?
 | 
			
		||||
//	* Enforce maximum number of records to print per node?
 | 
			
		||||
//	* Enforce maximum size in bytes allowed?
 | 
			
		||||
//	* As a heuristic, use less verbosity for equal nodes than unequal nodes.
 | 
			
		||||
// TODO: Enforce unique outputs?
 | 
			
		||||
//	* Avoid Stringer methods if it results in same output?
 | 
			
		||||
//	* Print pointer address if outputs still equal?
 | 
			
		||||
 | 
			
		||||
// numContextRecords is the number of surrounding equal records to print.
 | 
			
		||||
const numContextRecords = 2
 | 
			
		||||
 | 
			
		||||
@@ -71,19 +63,56 @@ func (opts formatOptions) WithTypeMode(t typeMode) formatOptions {
 | 
			
		||||
	opts.TypeMode = t
 | 
			
		||||
	return opts
 | 
			
		||||
}
 | 
			
		||||
func (opts formatOptions) WithVerbosity(level int) formatOptions {
 | 
			
		||||
	opts.VerbosityLevel = level
 | 
			
		||||
	opts.LimitVerbosity = true
 | 
			
		||||
	return opts
 | 
			
		||||
}
 | 
			
		||||
func (opts formatOptions) verbosity() uint {
 | 
			
		||||
	switch {
 | 
			
		||||
	case opts.VerbosityLevel < 0:
 | 
			
		||||
		return 0
 | 
			
		||||
	case opts.VerbosityLevel > 16:
 | 
			
		||||
		return 16 // some reasonable maximum to avoid shift overflow
 | 
			
		||||
	default:
 | 
			
		||||
		return uint(opts.VerbosityLevel)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const maxVerbosityPreset = 3
 | 
			
		||||
 | 
			
		||||
// verbosityPreset modifies the verbosity settings given an index
 | 
			
		||||
// between 0 and maxVerbosityPreset, inclusive.
 | 
			
		||||
func verbosityPreset(opts formatOptions, i int) formatOptions {
 | 
			
		||||
	opts.VerbosityLevel = int(opts.verbosity()) + 2*i
 | 
			
		||||
	if i > 0 {
 | 
			
		||||
		opts.AvoidStringer = true
 | 
			
		||||
	}
 | 
			
		||||
	if i >= maxVerbosityPreset {
 | 
			
		||||
		opts.PrintAddresses = true
 | 
			
		||||
		opts.QualifiedNames = true
 | 
			
		||||
	}
 | 
			
		||||
	return opts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatDiff converts a valueNode tree into a textNode tree, where the later
 | 
			
		||||
// is a textual representation of the differences detected in the former.
 | 
			
		||||
func (opts formatOptions) FormatDiff(v *valueNode) textNode {
 | 
			
		||||
func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) {
 | 
			
		||||
	if opts.DiffMode == diffIdentical {
 | 
			
		||||
		opts = opts.WithVerbosity(1)
 | 
			
		||||
	} else {
 | 
			
		||||
		opts = opts.WithVerbosity(3)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check whether we have specialized formatting for this node.
 | 
			
		||||
	// This is not necessary, but helpful for producing more readable outputs.
 | 
			
		||||
	if opts.CanFormatDiffSlice(v) {
 | 
			
		||||
		return opts.FormatDiffSlice(v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var withinSlice bool
 | 
			
		||||
	if v.parent != nil && (v.parent.Type.Kind() == reflect.Slice || v.parent.Type.Kind() == reflect.Array) {
 | 
			
		||||
		withinSlice = true
 | 
			
		||||
	var parentKind reflect.Kind
 | 
			
		||||
	if v.parent != nil && v.parent.TransformerName == "" {
 | 
			
		||||
		parentKind = v.parent.Type.Kind()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// For leaf nodes, format the value based on the reflect.Values alone.
 | 
			
		||||
@@ -92,8 +121,8 @@ func (opts formatOptions) FormatDiff(v *valueNode) textNode {
 | 
			
		||||
		case diffUnknown, diffIdentical:
 | 
			
		||||
			// Format Equal.
 | 
			
		||||
			if v.NumDiff == 0 {
 | 
			
		||||
				outx := opts.FormatValue(v.ValueX, withinSlice, visitedPointers{})
 | 
			
		||||
				outy := opts.FormatValue(v.ValueY, withinSlice, visitedPointers{})
 | 
			
		||||
				outx := opts.FormatValue(v.ValueX, parentKind, ptrs)
 | 
			
		||||
				outy := opts.FormatValue(v.ValueY, parentKind, ptrs)
 | 
			
		||||
				if v.NumIgnored > 0 && v.NumSame == 0 {
 | 
			
		||||
					return textEllipsis
 | 
			
		||||
				} else if outx.Len() < outy.Len() {
 | 
			
		||||
@@ -106,8 +135,13 @@ func (opts formatOptions) FormatDiff(v *valueNode) textNode {
 | 
			
		||||
			// Format unequal.
 | 
			
		||||
			assert(opts.DiffMode == diffUnknown)
 | 
			
		||||
			var list textList
 | 
			
		||||
			outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, withinSlice, visitedPointers{})
 | 
			
		||||
			outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, withinSlice, visitedPointers{})
 | 
			
		||||
			outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs)
 | 
			
		||||
			outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs)
 | 
			
		||||
			for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ {
 | 
			
		||||
				opts2 := verbosityPreset(opts, i).WithTypeMode(elideType)
 | 
			
		||||
				outx = opts2.FormatValue(v.ValueX, parentKind, ptrs)
 | 
			
		||||
				outy = opts2.FormatValue(v.ValueY, parentKind, ptrs)
 | 
			
		||||
			}
 | 
			
		||||
			if outx != nil {
 | 
			
		||||
				list = append(list, textRecord{Diff: '-', Value: outx})
 | 
			
		||||
			}
 | 
			
		||||
@@ -116,34 +150,57 @@ func (opts formatOptions) FormatDiff(v *valueNode) textNode {
 | 
			
		||||
			}
 | 
			
		||||
			return opts.WithTypeMode(emitType).FormatType(v.Type, list)
 | 
			
		||||
		case diffRemoved:
 | 
			
		||||
			return opts.FormatValue(v.ValueX, withinSlice, visitedPointers{})
 | 
			
		||||
			return opts.FormatValue(v.ValueX, parentKind, ptrs)
 | 
			
		||||
		case diffInserted:
 | 
			
		||||
			return opts.FormatValue(v.ValueY, withinSlice, visitedPointers{})
 | 
			
		||||
			return opts.FormatValue(v.ValueY, parentKind, ptrs)
 | 
			
		||||
		default:
 | 
			
		||||
			panic("invalid diff mode")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Register slice element to support cycle detection.
 | 
			
		||||
	if parentKind == reflect.Slice {
 | 
			
		||||
		ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true)
 | 
			
		||||
		defer ptrs.Pop()
 | 
			
		||||
		defer func() { out = wrapTrunkReferences(ptrRefs, out) }()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Descend into the child value node.
 | 
			
		||||
	if v.TransformerName != "" {
 | 
			
		||||
		out := opts.WithTypeMode(emitType).FormatDiff(v.Value)
 | 
			
		||||
		out = textWrap{"Inverse(" + v.TransformerName + ", ", out, ")"}
 | 
			
		||||
		out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs)
 | 
			
		||||
		out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"}
 | 
			
		||||
		return opts.FormatType(v.Type, out)
 | 
			
		||||
	} else {
 | 
			
		||||
		switch k := v.Type.Kind(); k {
 | 
			
		||||
		case reflect.Struct, reflect.Array, reflect.Slice, reflect.Map:
 | 
			
		||||
			return opts.FormatType(v.Type, opts.formatDiffList(v.Records, k))
 | 
			
		||||
		case reflect.Struct, reflect.Array, reflect.Slice:
 | 
			
		||||
			out = opts.formatDiffList(v.Records, k, ptrs)
 | 
			
		||||
			out = opts.FormatType(v.Type, out)
 | 
			
		||||
		case reflect.Map:
 | 
			
		||||
			// Register map to support cycle detection.
 | 
			
		||||
			ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false)
 | 
			
		||||
			defer ptrs.Pop()
 | 
			
		||||
 | 
			
		||||
			out = opts.formatDiffList(v.Records, k, ptrs)
 | 
			
		||||
			out = wrapTrunkReferences(ptrRefs, out)
 | 
			
		||||
			out = opts.FormatType(v.Type, out)
 | 
			
		||||
		case reflect.Ptr:
 | 
			
		||||
			return textWrap{"&", opts.FormatDiff(v.Value), ""}
 | 
			
		||||
			// Register pointer to support cycle detection.
 | 
			
		||||
			ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false)
 | 
			
		||||
			defer ptrs.Pop()
 | 
			
		||||
 | 
			
		||||
			out = opts.FormatDiff(v.Value, ptrs)
 | 
			
		||||
			out = wrapTrunkReferences(ptrRefs, out)
 | 
			
		||||
			out = &textWrap{Prefix: "&", Value: out}
 | 
			
		||||
		case reflect.Interface:
 | 
			
		||||
			return opts.WithTypeMode(emitType).FormatDiff(v.Value)
 | 
			
		||||
			out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs)
 | 
			
		||||
		default:
 | 
			
		||||
			panic(fmt.Sprintf("%v cannot have children", k))
 | 
			
		||||
		}
 | 
			
		||||
		return out
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) textNode {
 | 
			
		||||
func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode {
 | 
			
		||||
	// Derive record name based on the data structure kind.
 | 
			
		||||
	var name string
 | 
			
		||||
	var formatKey func(reflect.Value) string
 | 
			
		||||
@@ -159,7 +216,17 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		name = "entry"
 | 
			
		||||
		opts = opts.WithTypeMode(elideType)
 | 
			
		||||
		formatKey = formatMapKey
 | 
			
		||||
		formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	maxLen := -1
 | 
			
		||||
	if opts.LimitVerbosity {
 | 
			
		||||
		if opts.DiffMode == diffIdentical {
 | 
			
		||||
			maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
 | 
			
		||||
		} else {
 | 
			
		||||
			maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc...
 | 
			
		||||
		}
 | 
			
		||||
		opts.VerbosityLevel--
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle unification.
 | 
			
		||||
@@ -168,6 +235,11 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
 | 
			
		||||
		var list textList
 | 
			
		||||
		var deferredEllipsis bool // Add final "..." to indicate records were dropped
 | 
			
		||||
		for _, r := range recs {
 | 
			
		||||
			if len(list) == maxLen {
 | 
			
		||||
				deferredEllipsis = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Elide struct fields that are zero value.
 | 
			
		||||
			if k == reflect.Struct {
 | 
			
		||||
				var isZero bool
 | 
			
		||||
@@ -191,23 +263,31 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
 | 
			
		||||
				}
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if out := opts.FormatDiff(r.Value); out != nil {
 | 
			
		||||
			if out := opts.FormatDiff(r.Value, ptrs); out != nil {
 | 
			
		||||
				list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if deferredEllipsis {
 | 
			
		||||
			list.AppendEllipsis(diffStats{})
 | 
			
		||||
		}
 | 
			
		||||
		return textWrap{"{", list, "}"}
 | 
			
		||||
		return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
 | 
			
		||||
	case diffUnknown:
 | 
			
		||||
	default:
 | 
			
		||||
		panic("invalid diff mode")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle differencing.
 | 
			
		||||
	var numDiffs int
 | 
			
		||||
	var list textList
 | 
			
		||||
	var keys []reflect.Value // invariant: len(list) == len(keys)
 | 
			
		||||
	groups := coalesceAdjacentRecords(name, recs)
 | 
			
		||||
	maxGroup := diffStats{Name: name}
 | 
			
		||||
	for i, ds := range groups {
 | 
			
		||||
		if maxLen >= 0 && numDiffs >= maxLen {
 | 
			
		||||
			maxGroup = maxGroup.Append(ds)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Handle equal records.
 | 
			
		||||
		if ds.NumDiff() == 0 {
 | 
			
		||||
			// Compute the number of leading and trailing records to print.
 | 
			
		||||
@@ -231,16 +311,21 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
 | 
			
		||||
 | 
			
		||||
			// Format the equal values.
 | 
			
		||||
			for _, r := range recs[:numLo] {
 | 
			
		||||
				out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value)
 | 
			
		||||
				out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs)
 | 
			
		||||
				list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
 | 
			
		||||
				keys = append(keys, r.Key)
 | 
			
		||||
			}
 | 
			
		||||
			if numEqual > numLo+numHi {
 | 
			
		||||
				ds.NumIdentical -= numLo + numHi
 | 
			
		||||
				list.AppendEllipsis(ds)
 | 
			
		||||
				for len(keys) < len(list) {
 | 
			
		||||
					keys = append(keys, reflect.Value{})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			for _, r := range recs[numEqual-numHi : numEqual] {
 | 
			
		||||
				out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value)
 | 
			
		||||
				out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs)
 | 
			
		||||
				list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
 | 
			
		||||
				keys = append(keys, r.Key)
 | 
			
		||||
			}
 | 
			
		||||
			recs = recs[numEqual:]
 | 
			
		||||
			continue
 | 
			
		||||
@@ -252,24 +337,70 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
 | 
			
		||||
			case opts.CanFormatDiffSlice(r.Value):
 | 
			
		||||
				out := opts.FormatDiffSlice(r.Value)
 | 
			
		||||
				list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
 | 
			
		||||
				keys = append(keys, r.Key)
 | 
			
		||||
			case r.Value.NumChildren == r.Value.MaxDepth:
 | 
			
		||||
				outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value)
 | 
			
		||||
				outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value)
 | 
			
		||||
				outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs)
 | 
			
		||||
				outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs)
 | 
			
		||||
				for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ {
 | 
			
		||||
					opts2 := verbosityPreset(opts, i)
 | 
			
		||||
					outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs)
 | 
			
		||||
					outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs)
 | 
			
		||||
				}
 | 
			
		||||
				if outx != nil {
 | 
			
		||||
					list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx})
 | 
			
		||||
					keys = append(keys, r.Key)
 | 
			
		||||
				}
 | 
			
		||||
				if outy != nil {
 | 
			
		||||
					list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy})
 | 
			
		||||
					keys = append(keys, r.Key)
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
				out := opts.FormatDiff(r.Value)
 | 
			
		||||
				out := opts.FormatDiff(r.Value, ptrs)
 | 
			
		||||
				list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
 | 
			
		||||
				keys = append(keys, r.Key)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		recs = recs[ds.NumDiff():]
 | 
			
		||||
		numDiffs += ds.NumDiff()
 | 
			
		||||
	}
 | 
			
		||||
	if maxGroup.IsZero() {
 | 
			
		||||
		assert(len(recs) == 0)
 | 
			
		||||
	return textWrap{"{", list, "}"}
 | 
			
		||||
	} else {
 | 
			
		||||
		list.AppendEllipsis(maxGroup)
 | 
			
		||||
		for len(keys) < len(list) {
 | 
			
		||||
			keys = append(keys, reflect.Value{})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	assert(len(list) == len(keys))
 | 
			
		||||
 | 
			
		||||
	// For maps, the default formatting logic uses fmt.Stringer which may
 | 
			
		||||
	// produce ambiguous output. Avoid calling String to disambiguate.
 | 
			
		||||
	if k == reflect.Map {
 | 
			
		||||
		var ambiguous bool
 | 
			
		||||
		seenKeys := map[string]reflect.Value{}
 | 
			
		||||
		for i, currKey := range keys {
 | 
			
		||||
			if currKey.IsValid() {
 | 
			
		||||
				strKey := list[i].Key
 | 
			
		||||
				prevKey, seen := seenKeys[strKey]
 | 
			
		||||
				if seen && prevKey.CanInterface() && currKey.CanInterface() {
 | 
			
		||||
					ambiguous = prevKey.Interface() != currKey.Interface()
 | 
			
		||||
					if ambiguous {
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				seenKeys[strKey] = currKey
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if ambiguous {
 | 
			
		||||
			for i, k := range keys {
 | 
			
		||||
				if k.IsValid() {
 | 
			
		||||
					list[i].Key = formatMapKey(k, true, ptrs)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// coalesceAdjacentRecords coalesces the list of records into groups of
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										264
									
								
								vendor/github.com/google/go-cmp/cmp/report_references.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								vendor/github.com/google/go-cmp/cmp/report_references.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
// Copyright 2020, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/go-cmp/cmp/internal/flags"
 | 
			
		||||
	"github.com/google/go-cmp/cmp/internal/value"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	pointerDelimPrefix = "⟪"
 | 
			
		||||
	pointerDelimSuffix = "⟫"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// formatPointer prints the address of the pointer.
 | 
			
		||||
func formatPointer(p value.Pointer, withDelims bool) string {
 | 
			
		||||
	v := p.Uintptr()
 | 
			
		||||
	if flags.Deterministic {
 | 
			
		||||
		v = 0xdeadf00f // Only used for stable testing purposes
 | 
			
		||||
	}
 | 
			
		||||
	if withDelims {
 | 
			
		||||
		return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix
 | 
			
		||||
	}
 | 
			
		||||
	return formatHex(uint64(v))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pointerReferences is a stack of pointers visited so far.
 | 
			
		||||
type pointerReferences [][2]value.Pointer
 | 
			
		||||
 | 
			
		||||
func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) {
 | 
			
		||||
	if deref && vx.IsValid() {
 | 
			
		||||
		vx = vx.Addr()
 | 
			
		||||
	}
 | 
			
		||||
	if deref && vy.IsValid() {
 | 
			
		||||
		vy = vy.Addr()
 | 
			
		||||
	}
 | 
			
		||||
	switch d {
 | 
			
		||||
	case diffUnknown, diffIdentical:
 | 
			
		||||
		pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)}
 | 
			
		||||
	case diffRemoved:
 | 
			
		||||
		pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}}
 | 
			
		||||
	case diffInserted:
 | 
			
		||||
		pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)}
 | 
			
		||||
	}
 | 
			
		||||
	*ps = append(*ps, pp)
 | 
			
		||||
	return pp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) {
 | 
			
		||||
	p = value.PointerOf(v)
 | 
			
		||||
	for _, pp := range *ps {
 | 
			
		||||
		if p == pp[0] || p == pp[1] {
 | 
			
		||||
			return p, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	*ps = append(*ps, [2]value.Pointer{p, p})
 | 
			
		||||
	return p, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ps *pointerReferences) Pop() {
 | 
			
		||||
	*ps = (*ps)[:len(*ps)-1]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// trunkReferences is metadata for a textNode indicating that the sub-tree
 | 
			
		||||
// represents the value for either pointer in a pair of references.
 | 
			
		||||
type trunkReferences struct{ pp [2]value.Pointer }
 | 
			
		||||
 | 
			
		||||
// trunkReference is metadata for a textNode indicating that the sub-tree
 | 
			
		||||
// represents the value for the given pointer reference.
 | 
			
		||||
type trunkReference struct{ p value.Pointer }
 | 
			
		||||
 | 
			
		||||
// leafReference is metadata for a textNode indicating that the value is
 | 
			
		||||
// truncated as it refers to another part of the tree (i.e., a trunk).
 | 
			
		||||
type leafReference struct{ p value.Pointer }
 | 
			
		||||
 | 
			
		||||
func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode {
 | 
			
		||||
	switch {
 | 
			
		||||
	case pp[0].IsNil():
 | 
			
		||||
		return &textWrap{Value: s, Metadata: trunkReference{pp[1]}}
 | 
			
		||||
	case pp[1].IsNil():
 | 
			
		||||
		return &textWrap{Value: s, Metadata: trunkReference{pp[0]}}
 | 
			
		||||
	case pp[0] == pp[1]:
 | 
			
		||||
		return &textWrap{Value: s, Metadata: trunkReference{pp[0]}}
 | 
			
		||||
	default:
 | 
			
		||||
		return &textWrap{Value: s, Metadata: trunkReferences{pp}}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode {
 | 
			
		||||
	var prefix string
 | 
			
		||||
	if printAddress {
 | 
			
		||||
		prefix = formatPointer(p, true)
 | 
			
		||||
	}
 | 
			
		||||
	return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}}
 | 
			
		||||
}
 | 
			
		||||
func makeLeafReference(p value.Pointer, printAddress bool) textNode {
 | 
			
		||||
	out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"}
 | 
			
		||||
	var prefix string
 | 
			
		||||
	if printAddress {
 | 
			
		||||
		prefix = formatPointer(p, true)
 | 
			
		||||
	}
 | 
			
		||||
	return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// resolveReferences walks the textNode tree searching for any leaf reference
 | 
			
		||||
// metadata and resolves each against the corresponding trunk references.
 | 
			
		||||
// Since pointer addresses in memory are not particularly readable to the user,
 | 
			
		||||
// it replaces each pointer value with an arbitrary and unique reference ID.
 | 
			
		||||
func resolveReferences(s textNode) {
 | 
			
		||||
	var walkNodes func(textNode, func(textNode))
 | 
			
		||||
	walkNodes = func(s textNode, f func(textNode)) {
 | 
			
		||||
		f(s)
 | 
			
		||||
		switch s := s.(type) {
 | 
			
		||||
		case *textWrap:
 | 
			
		||||
			walkNodes(s.Value, f)
 | 
			
		||||
		case textList:
 | 
			
		||||
			for _, r := range s {
 | 
			
		||||
				walkNodes(r.Value, f)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Collect all trunks and leaves with reference metadata.
 | 
			
		||||
	var trunks, leaves []*textWrap
 | 
			
		||||
	walkNodes(s, func(s textNode) {
 | 
			
		||||
		if s, ok := s.(*textWrap); ok {
 | 
			
		||||
			switch s.Metadata.(type) {
 | 
			
		||||
			case leafReference:
 | 
			
		||||
				leaves = append(leaves, s)
 | 
			
		||||
			case trunkReference, trunkReferences:
 | 
			
		||||
				trunks = append(trunks, s)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// No leaf references to resolve.
 | 
			
		||||
	if len(leaves) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Collect the set of all leaf references to resolve.
 | 
			
		||||
	leafPtrs := make(map[value.Pointer]bool)
 | 
			
		||||
	for _, leaf := range leaves {
 | 
			
		||||
		leafPtrs[leaf.Metadata.(leafReference).p] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Collect the set of trunk pointers that are always paired together.
 | 
			
		||||
	// This allows us to assign a single ID to both pointers for brevity.
 | 
			
		||||
	// If a pointer in a pair ever occurs by itself or as a different pair,
 | 
			
		||||
	// then the pair is broken.
 | 
			
		||||
	pairedTrunkPtrs := make(map[value.Pointer]value.Pointer)
 | 
			
		||||
	unpair := func(p value.Pointer) {
 | 
			
		||||
		if !pairedTrunkPtrs[p].IsNil() {
 | 
			
		||||
			pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half
 | 
			
		||||
		}
 | 
			
		||||
		pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half
 | 
			
		||||
	}
 | 
			
		||||
	for _, trunk := range trunks {
 | 
			
		||||
		switch p := trunk.Metadata.(type) {
 | 
			
		||||
		case trunkReference:
 | 
			
		||||
			unpair(p.p) // standalone pointer cannot be part of a pair
 | 
			
		||||
		case trunkReferences:
 | 
			
		||||
			p0, ok0 := pairedTrunkPtrs[p.pp[0]]
 | 
			
		||||
			p1, ok1 := pairedTrunkPtrs[p.pp[1]]
 | 
			
		||||
			switch {
 | 
			
		||||
			case !ok0 && !ok1:
 | 
			
		||||
				// Register the newly seen pair.
 | 
			
		||||
				pairedTrunkPtrs[p.pp[0]] = p.pp[1]
 | 
			
		||||
				pairedTrunkPtrs[p.pp[1]] = p.pp[0]
 | 
			
		||||
			case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]:
 | 
			
		||||
				// Exact pair already seen; do nothing.
 | 
			
		||||
			default:
 | 
			
		||||
				// Pair conflicts with some other pair; break all pairs.
 | 
			
		||||
				unpair(p.pp[0])
 | 
			
		||||
				unpair(p.pp[1])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Correlate each pointer referenced by leaves to a unique identifier,
 | 
			
		||||
	// and print the IDs for each trunk that matches those pointers.
 | 
			
		||||
	var nextID uint
 | 
			
		||||
	ptrIDs := make(map[value.Pointer]uint)
 | 
			
		||||
	newID := func() uint {
 | 
			
		||||
		id := nextID
 | 
			
		||||
		nextID++
 | 
			
		||||
		return id
 | 
			
		||||
	}
 | 
			
		||||
	for _, trunk := range trunks {
 | 
			
		||||
		switch p := trunk.Metadata.(type) {
 | 
			
		||||
		case trunkReference:
 | 
			
		||||
			if print := leafPtrs[p.p]; print {
 | 
			
		||||
				id, ok := ptrIDs[p.p]
 | 
			
		||||
				if !ok {
 | 
			
		||||
					id = newID()
 | 
			
		||||
					ptrIDs[p.p] = id
 | 
			
		||||
				}
 | 
			
		||||
				trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id))
 | 
			
		||||
			}
 | 
			
		||||
		case trunkReferences:
 | 
			
		||||
			print0 := leafPtrs[p.pp[0]]
 | 
			
		||||
			print1 := leafPtrs[p.pp[1]]
 | 
			
		||||
			if print0 || print1 {
 | 
			
		||||
				id0, ok0 := ptrIDs[p.pp[0]]
 | 
			
		||||
				id1, ok1 := ptrIDs[p.pp[1]]
 | 
			
		||||
				isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0]
 | 
			
		||||
				if isPair {
 | 
			
		||||
					var id uint
 | 
			
		||||
					assert(ok0 == ok1) // must be seen together or not at all
 | 
			
		||||
					if ok0 {
 | 
			
		||||
						assert(id0 == id1) // must have the same ID
 | 
			
		||||
						id = id0
 | 
			
		||||
					} else {
 | 
			
		||||
						id = newID()
 | 
			
		||||
						ptrIDs[p.pp[0]] = id
 | 
			
		||||
						ptrIDs[p.pp[1]] = id
 | 
			
		||||
					}
 | 
			
		||||
					trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id))
 | 
			
		||||
				} else {
 | 
			
		||||
					if print0 && !ok0 {
 | 
			
		||||
						id0 = newID()
 | 
			
		||||
						ptrIDs[p.pp[0]] = id0
 | 
			
		||||
					}
 | 
			
		||||
					if print1 && !ok1 {
 | 
			
		||||
						id1 = newID()
 | 
			
		||||
						ptrIDs[p.pp[1]] = id1
 | 
			
		||||
					}
 | 
			
		||||
					switch {
 | 
			
		||||
					case print0 && print1:
 | 
			
		||||
						trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1))
 | 
			
		||||
					case print0:
 | 
			
		||||
						trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0))
 | 
			
		||||
					case print1:
 | 
			
		||||
						trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update all leaf references with the unique identifier.
 | 
			
		||||
	for _, leaf := range leaves {
 | 
			
		||||
		if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok {
 | 
			
		||||
			leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func formatReference(id uint) string {
 | 
			
		||||
	return fmt.Sprintf("ref#%d", id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateReferencePrefix(prefix, ref string) string {
 | 
			
		||||
	if prefix == "" {
 | 
			
		||||
		return pointerDelimPrefix + ref + pointerDelimSuffix
 | 
			
		||||
	}
 | 
			
		||||
	suffix := strings.TrimPrefix(prefix, pointerDelimPrefix)
 | 
			
		||||
	return pointerDelimPrefix + ref + ": " + suffix
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										285
									
								
								vendor/github.com/google/go-cmp/cmp/report_reflect.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										285
									
								
								vendor/github.com/google/go-cmp/cmp/report_reflect.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,17 +1,18 @@
 | 
			
		||||
// Copyright 2019, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/go-cmp/cmp/internal/flags"
 | 
			
		||||
	"github.com/google/go-cmp/cmp/internal/value"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -20,14 +21,22 @@ type formatValueOptions struct {
 | 
			
		||||
	// methods like error.Error or fmt.Stringer.String.
 | 
			
		||||
	AvoidStringer bool
 | 
			
		||||
 | 
			
		||||
	// ShallowPointers controls whether to avoid descending into pointers.
 | 
			
		||||
	// Useful when printing map keys, where pointer comparison is performed
 | 
			
		||||
	// on the pointer address rather than the pointed-at value.
 | 
			
		||||
	ShallowPointers bool
 | 
			
		||||
 | 
			
		||||
	// PrintAddresses controls whether to print the address of all pointers,
 | 
			
		||||
	// slice elements, and maps.
 | 
			
		||||
	PrintAddresses bool
 | 
			
		||||
 | 
			
		||||
	// QualifiedNames controls whether FormatType uses the fully qualified name
 | 
			
		||||
	// (including the full package path as opposed to just the package name).
 | 
			
		||||
	QualifiedNames bool
 | 
			
		||||
 | 
			
		||||
	// VerbosityLevel controls the amount of output to produce.
 | 
			
		||||
	// A higher value produces more output. A value of zero or lower produces
 | 
			
		||||
	// no output (represented using an ellipsis).
 | 
			
		||||
	// If LimitVerbosity is false, then the level is treated as infinite.
 | 
			
		||||
	VerbosityLevel int
 | 
			
		||||
 | 
			
		||||
	// LimitVerbosity specifies that formatting should respect VerbosityLevel.
 | 
			
		||||
	LimitVerbosity bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatType prints the type as if it were wrapping s.
 | 
			
		||||
@@ -44,12 +53,15 @@ func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
 | 
			
		||||
		default:
 | 
			
		||||
			return s
 | 
			
		||||
		}
 | 
			
		||||
		if opts.DiffMode == diffIdentical {
 | 
			
		||||
			return s // elide type for identical nodes
 | 
			
		||||
		}
 | 
			
		||||
	case elideType:
 | 
			
		||||
		return s
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Determine the type label, applying special handling for unnamed types.
 | 
			
		||||
	typeName := t.String()
 | 
			
		||||
	typeName := value.TypeString(t, opts.QualifiedNames)
 | 
			
		||||
	if t.Name() == "" {
 | 
			
		||||
		// According to Go grammar, certain type literals contain symbols that
 | 
			
		||||
		// do not strongly bind to the next lexicographical token (e.g., *T).
 | 
			
		||||
@@ -57,39 +69,77 @@ func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
 | 
			
		||||
		case reflect.Chan, reflect.Func, reflect.Ptr:
 | 
			
		||||
			typeName = "(" + typeName + ")"
 | 
			
		||||
		}
 | 
			
		||||
		typeName = strings.Replace(typeName, "struct {", "struct{", -1)
 | 
			
		||||
		typeName = strings.Replace(typeName, "interface {", "interface{", -1)
 | 
			
		||||
	}
 | 
			
		||||
	return &textWrap{Prefix: typeName, Value: wrapParens(s)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	// Avoid wrap the value in parenthesis if unnecessary.
 | 
			
		||||
	if s, ok := s.(textWrap); ok {
 | 
			
		||||
		hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")")
 | 
			
		||||
		hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}")
 | 
			
		||||
// wrapParens wraps s with a set of parenthesis, but avoids it if the
 | 
			
		||||
// wrapped node itself is already surrounded by a pair of parenthesis or braces.
 | 
			
		||||
// It handles unwrapping one level of pointer-reference nodes.
 | 
			
		||||
func wrapParens(s textNode) textNode {
 | 
			
		||||
	var refNode *textWrap
 | 
			
		||||
	if s2, ok := s.(*textWrap); ok {
 | 
			
		||||
		// Unwrap a single pointer reference node.
 | 
			
		||||
		switch s2.Metadata.(type) {
 | 
			
		||||
		case leafReference, trunkReference, trunkReferences:
 | 
			
		||||
			refNode = s2
 | 
			
		||||
			if s3, ok := refNode.Value.(*textWrap); ok {
 | 
			
		||||
				s2 = s3
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Already has delimiters that make parenthesis unnecessary.
 | 
			
		||||
		hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")")
 | 
			
		||||
		hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}")
 | 
			
		||||
		if hasParens || hasBraces {
 | 
			
		||||
			return textWrap{typeName, s, ""}
 | 
			
		||||
			return s
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return textWrap{typeName + "(", s, ")"}
 | 
			
		||||
	if refNode != nil {
 | 
			
		||||
		refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"}
 | 
			
		||||
		return s
 | 
			
		||||
	}
 | 
			
		||||
	return &textWrap{Prefix: "(", Value: s, Suffix: ")"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatValue prints the reflect.Value, taking extra care to avoid descending
 | 
			
		||||
// into pointers already in m. As pointers are visited, m is also updated.
 | 
			
		||||
func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visitedPointers) (out textNode) {
 | 
			
		||||
// into pointers already in ptrs. As pointers are visited, ptrs is also updated.
 | 
			
		||||
func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) {
 | 
			
		||||
	if !v.IsValid() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	t := v.Type()
 | 
			
		||||
 | 
			
		||||
	// Check slice element for cycles.
 | 
			
		||||
	if parentKind == reflect.Slice {
 | 
			
		||||
		ptrRef, visited := ptrs.Push(v.Addr())
 | 
			
		||||
		if visited {
 | 
			
		||||
			return makeLeafReference(ptrRef, false)
 | 
			
		||||
		}
 | 
			
		||||
		defer ptrs.Pop()
 | 
			
		||||
		defer func() { out = wrapTrunkReference(ptrRef, false, out) }()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check whether there is an Error or String method to call.
 | 
			
		||||
	if !opts.AvoidStringer && v.CanInterface() {
 | 
			
		||||
		// Avoid calling Error or String methods on nil receivers since many
 | 
			
		||||
		// implementations crash when doing so.
 | 
			
		||||
		if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
 | 
			
		||||
			var prefix, strVal string
 | 
			
		||||
			func() {
 | 
			
		||||
				// Swallow and ignore any panics from String or Error.
 | 
			
		||||
				defer func() { recover() }()
 | 
			
		||||
				switch v := v.Interface().(type) {
 | 
			
		||||
				case error:
 | 
			
		||||
				return textLine("e" + formatString(v.Error()))
 | 
			
		||||
					strVal = v.Error()
 | 
			
		||||
					prefix = "e"
 | 
			
		||||
				case fmt.Stringer:
 | 
			
		||||
				return textLine("s" + formatString(v.String()))
 | 
			
		||||
					strVal = v.String()
 | 
			
		||||
					prefix = "s"
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
			if prefix != "" {
 | 
			
		||||
				return opts.formatString(prefix, strVal)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -102,7 +152,6 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	var ptr string
 | 
			
		||||
	switch t.Kind() {
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		return textLine(fmt.Sprint(v.Bool()))
 | 
			
		||||
@@ -111,7 +160,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
 | 
			
		||||
	case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
			
		||||
		return textLine(fmt.Sprint(v.Uint()))
 | 
			
		||||
	case reflect.Uint8:
 | 
			
		||||
		if withinSlice {
 | 
			
		||||
		if parentKind == reflect.Slice || parentKind == reflect.Array {
 | 
			
		||||
			return textLine(formatHex(v.Uint()))
 | 
			
		||||
		}
 | 
			
		||||
		return textLine(fmt.Sprint(v.Uint()))
 | 
			
		||||
@@ -122,77 +171,121 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
 | 
			
		||||
	case reflect.Complex64, reflect.Complex128:
 | 
			
		||||
		return textLine(fmt.Sprint(v.Complex()))
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		return textLine(formatString(v.String()))
 | 
			
		||||
		return opts.formatString("", v.String())
 | 
			
		||||
	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
 | 
			
		||||
		return textLine(formatPointer(v))
 | 
			
		||||
		return textLine(formatPointer(value.PointerOf(v), true))
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		var list textList
 | 
			
		||||
		v := makeAddressable(v) // needed for retrieveUnexportedField
 | 
			
		||||
		maxLen := v.NumField()
 | 
			
		||||
		if opts.LimitVerbosity {
 | 
			
		||||
			maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
 | 
			
		||||
			opts.VerbosityLevel--
 | 
			
		||||
		}
 | 
			
		||||
		for i := 0; i < v.NumField(); i++ {
 | 
			
		||||
			vv := v.Field(i)
 | 
			
		||||
			if value.IsZero(vv) {
 | 
			
		||||
				continue // Elide fields with zero values
 | 
			
		||||
			}
 | 
			
		||||
			s := opts.WithTypeMode(autoType).FormatValue(vv, false, m)
 | 
			
		||||
			list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
 | 
			
		||||
			if len(list) == maxLen {
 | 
			
		||||
				list.AppendEllipsis(diffStats{})
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		return textWrap{"{", list, "}"}
 | 
			
		||||
			sf := t.Field(i)
 | 
			
		||||
			if supportExporters && !isExported(sf.Name) {
 | 
			
		||||
				vv = retrieveUnexportedField(v, sf, true)
 | 
			
		||||
			}
 | 
			
		||||
			s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs)
 | 
			
		||||
			list = append(list, textRecord{Key: sf.Name, Value: s})
 | 
			
		||||
		}
 | 
			
		||||
		return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			return textNil
 | 
			
		||||
		}
 | 
			
		||||
		if opts.PrintAddresses {
 | 
			
		||||
			ptr = formatPointer(v)
 | 
			
		||||
 | 
			
		||||
		// Check whether this is a []byte of text data.
 | 
			
		||||
		if t.Elem() == reflect.TypeOf(byte(0)) {
 | 
			
		||||
			b := v.Bytes()
 | 
			
		||||
			isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) && unicode.IsSpace(r) }
 | 
			
		||||
			if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 {
 | 
			
		||||
				out = opts.formatString("", string(b))
 | 
			
		||||
				return opts.WithTypeMode(emitType).FormatType(t, out)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case reflect.Array:
 | 
			
		||||
		maxLen := v.Len()
 | 
			
		||||
		if opts.LimitVerbosity {
 | 
			
		||||
			maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
 | 
			
		||||
			opts.VerbosityLevel--
 | 
			
		||||
		}
 | 
			
		||||
		var list textList
 | 
			
		||||
		for i := 0; i < v.Len(); i++ {
 | 
			
		||||
			vi := v.Index(i)
 | 
			
		||||
			if vi.CanAddr() { // Check for cyclic elements
 | 
			
		||||
				p := vi.Addr()
 | 
			
		||||
				if m.Visit(p) {
 | 
			
		||||
					var out textNode
 | 
			
		||||
					out = textLine(formatPointer(p))
 | 
			
		||||
					out = opts.WithTypeMode(emitType).FormatType(p.Type(), out)
 | 
			
		||||
					out = textWrap{"*", out, ""}
 | 
			
		||||
					list = append(list, textRecord{Value: out})
 | 
			
		||||
					continue
 | 
			
		||||
			if len(list) == maxLen {
 | 
			
		||||
				list.AppendEllipsis(diffStats{})
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			}
 | 
			
		||||
			s := opts.WithTypeMode(elideType).FormatValue(vi, true, m)
 | 
			
		||||
			s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs)
 | 
			
		||||
			list = append(list, textRecord{Value: s})
 | 
			
		||||
		}
 | 
			
		||||
		return textWrap{ptr + "{", list, "}"}
 | 
			
		||||
 | 
			
		||||
		out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
 | 
			
		||||
		if t.Kind() == reflect.Slice && opts.PrintAddresses {
 | 
			
		||||
			header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap())
 | 
			
		||||
			out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out}
 | 
			
		||||
		}
 | 
			
		||||
		return out
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			return textNil
 | 
			
		||||
		}
 | 
			
		||||
		if m.Visit(v) {
 | 
			
		||||
			return textLine(formatPointer(v))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check pointer for cycles.
 | 
			
		||||
		ptrRef, visited := ptrs.Push(v)
 | 
			
		||||
		if visited {
 | 
			
		||||
			return makeLeafReference(ptrRef, opts.PrintAddresses)
 | 
			
		||||
		}
 | 
			
		||||
		defer ptrs.Pop()
 | 
			
		||||
 | 
			
		||||
		maxLen := v.Len()
 | 
			
		||||
		if opts.LimitVerbosity {
 | 
			
		||||
			maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
 | 
			
		||||
			opts.VerbosityLevel--
 | 
			
		||||
		}
 | 
			
		||||
		var list textList
 | 
			
		||||
		for _, k := range value.SortKeys(v.MapKeys()) {
 | 
			
		||||
			sk := formatMapKey(k)
 | 
			
		||||
			sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), false, m)
 | 
			
		||||
			if len(list) == maxLen {
 | 
			
		||||
				list.AppendEllipsis(diffStats{})
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			sk := formatMapKey(k, false, ptrs)
 | 
			
		||||
			sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs)
 | 
			
		||||
			list = append(list, textRecord{Key: sk, Value: sv})
 | 
			
		||||
		}
 | 
			
		||||
		if opts.PrintAddresses {
 | 
			
		||||
			ptr = formatPointer(v)
 | 
			
		||||
		}
 | 
			
		||||
		return textWrap{ptr + "{", list, "}"}
 | 
			
		||||
 | 
			
		||||
		out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
 | 
			
		||||
		out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
 | 
			
		||||
		return out
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			return textNil
 | 
			
		||||
		}
 | 
			
		||||
		if m.Visit(v) || opts.ShallowPointers {
 | 
			
		||||
			return textLine(formatPointer(v))
 | 
			
		||||
		}
 | 
			
		||||
		if opts.PrintAddresses {
 | 
			
		||||
			ptr = formatPointer(v)
 | 
			
		||||
 | 
			
		||||
		// Check pointer for cycles.
 | 
			
		||||
		ptrRef, visited := ptrs.Push(v)
 | 
			
		||||
		if visited {
 | 
			
		||||
			out = makeLeafReference(ptrRef, opts.PrintAddresses)
 | 
			
		||||
			return &textWrap{Prefix: "&", Value: out}
 | 
			
		||||
		}
 | 
			
		||||
		defer ptrs.Pop()
 | 
			
		||||
 | 
			
		||||
		skipType = true // Let the underlying value print the type instead
 | 
			
		||||
		return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), false, m), ""}
 | 
			
		||||
		out = opts.FormatValue(v.Elem(), t.Kind(), ptrs)
 | 
			
		||||
		out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
 | 
			
		||||
		out = &textWrap{Prefix: "&", Value: out}
 | 
			
		||||
		return out
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			return textNil
 | 
			
		||||
@@ -200,19 +293,67 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
 | 
			
		||||
		// Interfaces accept different concrete types,
 | 
			
		||||
		// so configure the underlying value to explicitly print the type.
 | 
			
		||||
		skipType = true // Print the concrete type instead
 | 
			
		||||
		return opts.WithTypeMode(emitType).FormatValue(v.Elem(), false, m)
 | 
			
		||||
		return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs)
 | 
			
		||||
	default:
 | 
			
		||||
		panic(fmt.Sprintf("%v kind not handled", v.Kind()))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (opts formatOptions) formatString(prefix, s string) textNode {
 | 
			
		||||
	maxLen := len(s)
 | 
			
		||||
	maxLines := strings.Count(s, "\n") + 1
 | 
			
		||||
	if opts.LimitVerbosity {
 | 
			
		||||
		maxLen = (1 << opts.verbosity()) << 5   // 32, 64, 128, 256, etc...
 | 
			
		||||
		maxLines = (1 << opts.verbosity()) << 2 //  4, 8, 16, 32, 64, etc...
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// For multiline strings, use the triple-quote syntax,
 | 
			
		||||
	// but only use it when printing removed or inserted nodes since
 | 
			
		||||
	// we only want the extra verbosity for those cases.
 | 
			
		||||
	lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n")
 | 
			
		||||
	isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+')
 | 
			
		||||
	for i := 0; i < len(lines) && isTripleQuoted; i++ {
 | 
			
		||||
		lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
 | 
			
		||||
		isPrintable := func(r rune) bool {
 | 
			
		||||
			return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable
 | 
			
		||||
		}
 | 
			
		||||
		line := lines[i]
 | 
			
		||||
		isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen
 | 
			
		||||
	}
 | 
			
		||||
	if isTripleQuoted {
 | 
			
		||||
		var list textList
 | 
			
		||||
		list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true})
 | 
			
		||||
		for i, line := range lines {
 | 
			
		||||
			if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 {
 | 
			
		||||
				comment := commentString(fmt.Sprintf("%d elided lines", numElided))
 | 
			
		||||
				list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment})
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true})
 | 
			
		||||
		}
 | 
			
		||||
		list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true})
 | 
			
		||||
		return &textWrap{Prefix: "(", Value: list, Suffix: ")"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Format the string as a single-line quoted string.
 | 
			
		||||
	if len(s) > maxLen+len(textEllipsis) {
 | 
			
		||||
		return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis))
 | 
			
		||||
	}
 | 
			
		||||
	return textLine(prefix + formatString(s))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// formatMapKey formats v as if it were a map key.
 | 
			
		||||
// The result is guaranteed to be a single line.
 | 
			
		||||
func formatMapKey(v reflect.Value) string {
 | 
			
		||||
func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string {
 | 
			
		||||
	var opts formatOptions
 | 
			
		||||
	opts.DiffMode = diffIdentical
 | 
			
		||||
	opts.TypeMode = elideType
 | 
			
		||||
	opts.ShallowPointers = true
 | 
			
		||||
	s := opts.FormatValue(v, false, visitedPointers{}).String()
 | 
			
		||||
	opts.PrintAddresses = disambiguate
 | 
			
		||||
	opts.AvoidStringer = disambiguate
 | 
			
		||||
	opts.QualifiedNames = disambiguate
 | 
			
		||||
	opts.VerbosityLevel = maxVerbosityPreset
 | 
			
		||||
	opts.LimitVerbosity = true
 | 
			
		||||
	s := opts.FormatValue(v, reflect.Map, ptrs).String()
 | 
			
		||||
	return strings.TrimSpace(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -230,7 +371,7 @@ func formatString(s string) string {
 | 
			
		||||
	rawInvalid := func(r rune) bool {
 | 
			
		||||
		return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
 | 
			
		||||
	}
 | 
			
		||||
	if strings.IndexFunc(s, rawInvalid) < 0 {
 | 
			
		||||
	if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 {
 | 
			
		||||
		return "`" + s + "`"
 | 
			
		||||
	}
 | 
			
		||||
	return qs
 | 
			
		||||
@@ -259,23 +400,3 @@ func formatHex(u uint64) string {
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf(f, u)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// formatPointer prints the address of the pointer.
 | 
			
		||||
func formatPointer(v reflect.Value) string {
 | 
			
		||||
	p := v.Pointer()
 | 
			
		||||
	if flags.Deterministic {
 | 
			
		||||
		p = 0xdeadf00f // Only used for stable testing purposes
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("⟪0x%x⟫", p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type visitedPointers map[value.Pointer]struct{}
 | 
			
		||||
 | 
			
		||||
// Visit inserts pointer v into the visited map and reports whether it had
 | 
			
		||||
// already been visited before.
 | 
			
		||||
func (m visitedPointers) Visit(v reflect.Value) bool {
 | 
			
		||||
	p := value.PointerOf(v)
 | 
			
		||||
	_, visited := m[p]
 | 
			
		||||
	m[p] = struct{}{}
 | 
			
		||||
	return visited
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										131
									
								
								vendor/github.com/google/go-cmp/cmp/report_slices.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										131
									
								
								vendor/github.com/google/go-cmp/cmp/report_slices.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2019, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
@@ -8,6 +8,7 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
@@ -23,11 +24,25 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
 | 
			
		||||
		return false // Must be formatting in diff mode
 | 
			
		||||
	case v.NumDiff == 0:
 | 
			
		||||
		return false // No differences detected
 | 
			
		||||
	case v.NumIgnored+v.NumCompared+v.NumTransformed > 0:
 | 
			
		||||
		// TODO: Handle the case where someone uses bytes.Equal on a large slice.
 | 
			
		||||
		return false // Some custom option was used to determined equality
 | 
			
		||||
	case !v.ValueX.IsValid() || !v.ValueY.IsValid():
 | 
			
		||||
		return false // Both values must be valid
 | 
			
		||||
	case v.Type.Kind() == reflect.Slice && (v.ValueX.Len() == 0 || v.ValueY.Len() == 0):
 | 
			
		||||
		return false // Both slice values have to be non-empty
 | 
			
		||||
	case v.NumIgnored > 0:
 | 
			
		||||
		return false // Some ignore option was used
 | 
			
		||||
	case v.NumTransformed > 0:
 | 
			
		||||
		return false // Some transform option was used
 | 
			
		||||
	case v.NumCompared > 1:
 | 
			
		||||
		return false // More than one comparison was used
 | 
			
		||||
	case v.NumCompared == 1 && v.Type.Name() != "":
 | 
			
		||||
		// The need for cmp to check applicability of options on every element
 | 
			
		||||
		// in a slice is a significant performance detriment for large []byte.
 | 
			
		||||
		// The workaround is to specify Comparer(bytes.Equal),
 | 
			
		||||
		// which enables cmp to compare []byte more efficiently.
 | 
			
		||||
		// If they differ, we still want to provide batched diffing.
 | 
			
		||||
		// The logic disallows named types since they tend to have their own
 | 
			
		||||
		// String method, with nicer formatting than what this provides.
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch t := v.Type; t.Kind() {
 | 
			
		||||
@@ -82,7 +97,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
 | 
			
		||||
	}
 | 
			
		||||
	if isText || isBinary {
 | 
			
		||||
		var numLines, lastLineIdx, maxLineLen int
 | 
			
		||||
		isBinary = false
 | 
			
		||||
		isBinary = !utf8.ValidString(sx) || !utf8.ValidString(sy)
 | 
			
		||||
		for i, r := range sx + sy {
 | 
			
		||||
			if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError {
 | 
			
		||||
				isBinary = true
 | 
			
		||||
@@ -97,7 +112,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		isText = !isBinary
 | 
			
		||||
		isLinedText = isText && numLines >= 4 && maxLineLen <= 256
 | 
			
		||||
		isLinedText = isText && numLines >= 4 && maxLineLen <= 1024
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Format the string into printable records.
 | 
			
		||||
@@ -117,6 +132,83 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		delim = "\n"
 | 
			
		||||
 | 
			
		||||
		// If possible, use a custom triple-quote (""") syntax for printing
 | 
			
		||||
		// differences in a string literal. This format is more readable,
 | 
			
		||||
		// but has edge-cases where differences are visually indistinguishable.
 | 
			
		||||
		// This format is avoided under the following conditions:
 | 
			
		||||
		//	• A line starts with `"""`
 | 
			
		||||
		//	• A line starts with "..."
 | 
			
		||||
		//	• A line contains non-printable characters
 | 
			
		||||
		//	• Adjacent different lines differ only by whitespace
 | 
			
		||||
		//
 | 
			
		||||
		// For example:
 | 
			
		||||
		//		"""
 | 
			
		||||
		//		... // 3 identical lines
 | 
			
		||||
		//		foo
 | 
			
		||||
		//		bar
 | 
			
		||||
		//	-	baz
 | 
			
		||||
		//	+	BAZ
 | 
			
		||||
		//		"""
 | 
			
		||||
		isTripleQuoted := true
 | 
			
		||||
		prevRemoveLines := map[string]bool{}
 | 
			
		||||
		prevInsertLines := map[string]bool{}
 | 
			
		||||
		var list2 textList
 | 
			
		||||
		list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true})
 | 
			
		||||
		for _, r := range list {
 | 
			
		||||
			if !r.Value.Equal(textEllipsis) {
 | 
			
		||||
				line, _ := strconv.Unquote(string(r.Value.(textLine)))
 | 
			
		||||
				line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
 | 
			
		||||
				normLine := strings.Map(func(r rune) rune {
 | 
			
		||||
					if unicode.IsSpace(r) {
 | 
			
		||||
						return -1 // drop whitespace to avoid visually indistinguishable output
 | 
			
		||||
					}
 | 
			
		||||
					return r
 | 
			
		||||
				}, line)
 | 
			
		||||
				isPrintable := func(r rune) bool {
 | 
			
		||||
					return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable
 | 
			
		||||
				}
 | 
			
		||||
				isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == ""
 | 
			
		||||
				switch r.Diff {
 | 
			
		||||
				case diffRemoved:
 | 
			
		||||
					isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine]
 | 
			
		||||
					prevRemoveLines[normLine] = true
 | 
			
		||||
				case diffInserted:
 | 
			
		||||
					isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine]
 | 
			
		||||
					prevInsertLines[normLine] = true
 | 
			
		||||
				}
 | 
			
		||||
				if !isTripleQuoted {
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				r.Value = textLine(line)
 | 
			
		||||
				r.ElideComma = true
 | 
			
		||||
			}
 | 
			
		||||
			if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group
 | 
			
		||||
				prevRemoveLines = map[string]bool{}
 | 
			
		||||
				prevInsertLines = map[string]bool{}
 | 
			
		||||
			}
 | 
			
		||||
			list2 = append(list2, r)
 | 
			
		||||
		}
 | 
			
		||||
		if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 {
 | 
			
		||||
			list2 = list2[:len(list2)-1] // elide single empty line at the end
 | 
			
		||||
		}
 | 
			
		||||
		list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true})
 | 
			
		||||
		if isTripleQuoted {
 | 
			
		||||
			var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"}
 | 
			
		||||
			switch t.Kind() {
 | 
			
		||||
			case reflect.String:
 | 
			
		||||
				if t != reflect.TypeOf(string("")) {
 | 
			
		||||
					out = opts.FormatType(t, out)
 | 
			
		||||
				}
 | 
			
		||||
			case reflect.Slice:
 | 
			
		||||
				// Always emit type for slices since the triple-quote syntax
 | 
			
		||||
				// looks like a string (not a slice).
 | 
			
		||||
				opts = opts.WithTypeMode(emitType)
 | 
			
		||||
				out = opts.FormatType(t, out)
 | 
			
		||||
			}
 | 
			
		||||
			return out
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	// If the text appears to be single-lined text,
 | 
			
		||||
	// then perform differencing in approximately fixed-sized chunks.
 | 
			
		||||
	// The output is printed as quoted strings.
 | 
			
		||||
@@ -129,6 +221,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		delim = ""
 | 
			
		||||
 | 
			
		||||
	// If the text appears to be binary data,
 | 
			
		||||
	// then perform differencing in approximately fixed-sized chunks.
 | 
			
		||||
	// The output is inspired by hexdump.
 | 
			
		||||
@@ -145,6 +238,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
 | 
			
		||||
				return textRecord{Diff: d, Value: textLine(s), Comment: comment}
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
	// For all other slices of primitive types,
 | 
			
		||||
	// then perform differencing in approximately fixed-sized chunks.
 | 
			
		||||
	// The size of each chunk depends on the width of the element kind.
 | 
			
		||||
@@ -187,7 +281,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Wrap the output with appropriate type information.
 | 
			
		||||
	var out textNode = textWrap{"{", list, "}"}
 | 
			
		||||
	var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
 | 
			
		||||
	if !isText {
 | 
			
		||||
		// The "{...}" byte-sequence literal is not valid Go syntax for strings.
 | 
			
		||||
		// Emit the type for extra clarity (e.g. "string{...}").
 | 
			
		||||
@@ -198,12 +292,12 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
 | 
			
		||||
	}
 | 
			
		||||
	switch t.Kind() {
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		out = textWrap{"strings.Join(", out, fmt.Sprintf(", %q)", delim)}
 | 
			
		||||
		out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)}
 | 
			
		||||
		if t != reflect.TypeOf(string("")) {
 | 
			
		||||
			out = opts.FormatType(t, out)
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		out = textWrap{"bytes.Join(", out, fmt.Sprintf(", %q)", delim)}
 | 
			
		||||
		out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)}
 | 
			
		||||
		if t != reflect.TypeOf([]byte(nil)) {
 | 
			
		||||
			out = opts.FormatType(t, out)
 | 
			
		||||
		}
 | 
			
		||||
@@ -244,9 +338,22 @@ func (opts formatOptions) formatDiffSlice(
 | 
			
		||||
		return n0 - v.Len()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var numDiffs int
 | 
			
		||||
	maxLen := -1
 | 
			
		||||
	if opts.LimitVerbosity {
 | 
			
		||||
		maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc...
 | 
			
		||||
		opts.VerbosityLevel--
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	groups := coalesceAdjacentEdits(name, es)
 | 
			
		||||
	groups = coalesceInterveningIdentical(groups, chunkSize/4)
 | 
			
		||||
	maxGroup := diffStats{Name: name}
 | 
			
		||||
	for i, ds := range groups {
 | 
			
		||||
		if maxLen >= 0 && numDiffs >= maxLen {
 | 
			
		||||
			maxGroup = maxGroup.Append(ds)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Print equal.
 | 
			
		||||
		if ds.NumDiff() == 0 {
 | 
			
		||||
			// Compute the number of leading and trailing equal bytes to print.
 | 
			
		||||
@@ -275,12 +382,18 @@ func (opts formatOptions) formatDiffSlice(
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Print unequal.
 | 
			
		||||
		len0 := len(list)
 | 
			
		||||
		nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved)
 | 
			
		||||
		vx = vx.Slice(nx, vx.Len())
 | 
			
		||||
		ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted)
 | 
			
		||||
		vy = vy.Slice(ny, vy.Len())
 | 
			
		||||
		numDiffs += len(list) - len0
 | 
			
		||||
	}
 | 
			
		||||
	if maxGroup.IsZero() {
 | 
			
		||||
		assert(vx.Len() == 0 && vy.Len() == 0)
 | 
			
		||||
	} else {
 | 
			
		||||
		list.AppendEllipsis(maxGroup)
 | 
			
		||||
	}
 | 
			
		||||
	return list
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								vendor/github.com/google/go-cmp/cmp/report_text.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										74
									
								
								vendor/github.com/google/go-cmp/cmp/report_text.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2019, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
@@ -10,12 +10,15 @@ import (
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/go-cmp/cmp/internal/flags"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
 | 
			
		||||
 | 
			
		||||
const maxColumnLength = 80
 | 
			
		||||
 | 
			
		||||
type indentMode int
 | 
			
		||||
 | 
			
		||||
func (n indentMode) appendIndent(b []byte, d diffMode) []byte {
 | 
			
		||||
@@ -94,18 +97,19 @@ type textWrap struct {
 | 
			
		||||
	Prefix   string      // e.g., "bytes.Buffer{"
 | 
			
		||||
	Value    textNode    // textWrap | textList | textLine
 | 
			
		||||
	Suffix   string      // e.g., "}"
 | 
			
		||||
	Metadata interface{} // arbitrary metadata; has no effect on formatting
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s textWrap) Len() int {
 | 
			
		||||
func (s *textWrap) Len() int {
 | 
			
		||||
	return len(s.Prefix) + s.Value.Len() + len(s.Suffix)
 | 
			
		||||
}
 | 
			
		||||
func (s1 textWrap) Equal(s2 textNode) bool {
 | 
			
		||||
	if s2, ok := s2.(textWrap); ok {
 | 
			
		||||
func (s1 *textWrap) Equal(s2 textNode) bool {
 | 
			
		||||
	if s2, ok := s2.(*textWrap); ok {
 | 
			
		||||
		return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
func (s textWrap) String() string {
 | 
			
		||||
func (s *textWrap) String() string {
 | 
			
		||||
	var d diffMode
 | 
			
		||||
	var n indentMode
 | 
			
		||||
	_, s2 := s.formatCompactTo(nil, d)
 | 
			
		||||
@@ -114,7 +118,7 @@ func (s textWrap) String() string {
 | 
			
		||||
	b = append(b, '\n')              // Trailing newline
 | 
			
		||||
	return string(b)
 | 
			
		||||
}
 | 
			
		||||
func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
 | 
			
		||||
func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
 | 
			
		||||
	n0 := len(b) // Original buffer length
 | 
			
		||||
	b = append(b, s.Prefix...)
 | 
			
		||||
	b, s.Value = s.Value.formatCompactTo(b, d)
 | 
			
		||||
@@ -124,7 +128,7 @@ func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
 | 
			
		||||
	}
 | 
			
		||||
	return b, s
 | 
			
		||||
}
 | 
			
		||||
func (s textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
 | 
			
		||||
func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
 | 
			
		||||
	b = append(b, s.Prefix...)
 | 
			
		||||
	b = s.Value.formatExpandedTo(b, d, n)
 | 
			
		||||
	b = append(b, s.Suffix...)
 | 
			
		||||
@@ -139,6 +143,7 @@ type textRecord struct {
 | 
			
		||||
	Diff       diffMode     // e.g., 0 or '-' or '+'
 | 
			
		||||
	Key        string       // e.g., "MyField"
 | 
			
		||||
	Value      textNode     // textWrap | textLine
 | 
			
		||||
	ElideComma bool         // avoid trailing comma
 | 
			
		||||
	Comment    fmt.Stringer // e.g., "6 identical fields"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -146,12 +151,12 @@ type textRecord struct {
 | 
			
		||||
// exists at the end. If cs is non-zero it coalesces the statistics with the
 | 
			
		||||
// previous diffStats.
 | 
			
		||||
func (s *textList) AppendEllipsis(ds diffStats) {
 | 
			
		||||
	hasStats := ds != diffStats{}
 | 
			
		||||
	hasStats := !ds.IsZero()
 | 
			
		||||
	if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) {
 | 
			
		||||
		if hasStats {
 | 
			
		||||
			*s = append(*s, textRecord{Value: textEllipsis, Comment: ds})
 | 
			
		||||
			*s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds})
 | 
			
		||||
		} else {
 | 
			
		||||
			*s = append(*s, textRecord{Value: textEllipsis})
 | 
			
		||||
			*s = append(*s, textRecord{Value: textEllipsis, ElideComma: true})
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -191,7 +196,7 @@ func (s1 textList) Equal(s2 textNode) bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s textList) String() string {
 | 
			
		||||
	return textWrap{"{", s, "}"}.String()
 | 
			
		||||
	return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
 | 
			
		||||
@@ -221,7 +226,7 @@ func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
 | 
			
		||||
	}
 | 
			
		||||
	// Force multi-lined output when printing a removed/inserted node that
 | 
			
		||||
	// is sufficiently long.
 | 
			
		||||
	if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > 80 {
 | 
			
		||||
	if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength {
 | 
			
		||||
		multiLine = true
 | 
			
		||||
	}
 | 
			
		||||
	if !multiLine {
 | 
			
		||||
@@ -236,16 +241,50 @@ func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
 | 
			
		||||
			_, isLine := r.Value.(textLine)
 | 
			
		||||
			return r.Key == "" || !isLine
 | 
			
		||||
		},
 | 
			
		||||
		func(r textRecord) int { return len(r.Key) },
 | 
			
		||||
		func(r textRecord) int { return utf8.RuneCountInString(r.Key) },
 | 
			
		||||
	)
 | 
			
		||||
	alignValueLens := s.alignLens(
 | 
			
		||||
		func(r textRecord) bool {
 | 
			
		||||
			_, isLine := r.Value.(textLine)
 | 
			
		||||
			return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil
 | 
			
		||||
		},
 | 
			
		||||
		func(r textRecord) int { return len(r.Value.(textLine)) },
 | 
			
		||||
		func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) },
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Format lists of simple lists in a batched form.
 | 
			
		||||
	// If the list is sequence of only textLine values,
 | 
			
		||||
	// then batch multiple values on a single line.
 | 
			
		||||
	var isSimple bool
 | 
			
		||||
	for _, r := range s {
 | 
			
		||||
		_, isLine := r.Value.(textLine)
 | 
			
		||||
		isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil
 | 
			
		||||
		if !isSimple {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if isSimple {
 | 
			
		||||
		n++
 | 
			
		||||
		var batch []byte
 | 
			
		||||
		emitBatch := func() {
 | 
			
		||||
			if len(batch) > 0 {
 | 
			
		||||
				b = n.appendIndent(append(b, '\n'), d)
 | 
			
		||||
				b = append(b, bytes.TrimRight(batch, " ")...)
 | 
			
		||||
				batch = batch[:0]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, r := range s {
 | 
			
		||||
			line := r.Value.(textLine)
 | 
			
		||||
			if len(batch)+len(line)+len(", ") > maxColumnLength {
 | 
			
		||||
				emitBatch()
 | 
			
		||||
			}
 | 
			
		||||
			batch = append(batch, line...)
 | 
			
		||||
			batch = append(batch, ", "...)
 | 
			
		||||
		}
 | 
			
		||||
		emitBatch()
 | 
			
		||||
		n--
 | 
			
		||||
		return n.appendIndent(append(b, '\n'), d)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Format the list as a multi-lined output.
 | 
			
		||||
	n++
 | 
			
		||||
	for i, r := range s {
 | 
			
		||||
@@ -256,7 +295,7 @@ func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
 | 
			
		||||
		b = alignKeyLens[i].appendChar(b, ' ')
 | 
			
		||||
 | 
			
		||||
		b = r.Value.formatExpandedTo(b, d|r.Diff, n)
 | 
			
		||||
		if !r.Value.Equal(textEllipsis) {
 | 
			
		||||
		if !r.ElideComma {
 | 
			
		||||
			b = append(b, ',')
 | 
			
		||||
		}
 | 
			
		||||
		b = alignValueLens[i].appendChar(b, ' ')
 | 
			
		||||
@@ -332,6 +371,11 @@ type diffStats struct {
 | 
			
		||||
	NumModified  int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s diffStats) IsZero() bool {
 | 
			
		||||
	s.Name = ""
 | 
			
		||||
	return s == diffStats{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s diffStats) NumDiff() int {
 | 
			
		||||
	return s.NumRemoved + s.NumInserted + s.NumModified
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/report_value.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-cmp/cmp/report_value.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
// Copyright 2019, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE.md file.
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# github.com/google/go-cmp v0.4.1
 | 
			
		||||
# github.com/google/go-cmp v0.5.4
 | 
			
		||||
## explicit
 | 
			
		||||
github.com/google/go-cmp/cmp
 | 
			
		||||
github.com/google/go-cmp/cmp/internal/diff
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user