aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/go-cmp/cmp/path.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/google/go-cmp/cmp/path.go')
-rw-r--r--vendor/github.com/google/go-cmp/cmp/path.go309
1 files changed, 309 insertions, 0 deletions
diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go
new file mode 100644
index 00000000..c08a3cf8
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/path.go
@@ -0,0 +1,309 @@
+// 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.
+
+package cmp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+type (
+ // Path is a list of PathSteps describing the sequence of operations to get
+ // from some root type to the current position in the value tree.
+ // The first Path element is always an operation-less PathStep that exists
+ // simply to identify the initial type.
+ //
+ // When traversing structs with embedded structs, the embedded struct will
+ // always be accessed as a field before traversing the fields of the
+ // embedded struct themselves. That is, an exported field from the
+ // embedded struct will never be accessed directly from the parent struct.
+ Path []PathStep
+
+ // PathStep is a union-type for specific operations to traverse
+ // a value's tree structure. Users of this package never need to implement
+ // these types as values of this type will be returned by this package.
+ PathStep interface {
+ String() string
+ Type() reflect.Type // Resulting type after performing the path step
+ isPathStep()
+ }
+
+ // SliceIndex is an index operation on a slice or array at some index Key.
+ SliceIndex interface {
+ PathStep
+ Key() int // May return -1 if in a split state
+
+ // SplitKeys returns the indexes for indexing into slices in the
+ // x and y values, respectively. These indexes may differ due to the
+ // insertion or removal of an element in one of the slices, causing
+ // all of the indexes to be shifted. If an index is -1, then that
+ // indicates that the element does not exist in the associated slice.
+ //
+ // Key is guaranteed to return -1 if and only if the indexes returned
+ // by SplitKeys are not the same. SplitKeys will never return -1 for
+ // both indexes.
+ SplitKeys() (x int, y int)
+
+ isSliceIndex()
+ }
+ // MapIndex is an index operation on a map at some index Key.
+ MapIndex interface {
+ PathStep
+ Key() reflect.Value
+ isMapIndex()
+ }
+ // TypeAssertion represents a type assertion on an interface.
+ TypeAssertion interface {
+ PathStep
+ isTypeAssertion()
+ }
+ // StructField represents a struct field access on a field called Name.
+ StructField interface {
+ PathStep
+ Name() string
+ Index() int
+ isStructField()
+ }
+ // Indirect represents pointer indirection on the parent type.
+ Indirect interface {
+ PathStep
+ isIndirect()
+ }
+ // Transform is a transformation from the parent type to the current type.
+ Transform interface {
+ PathStep
+ Name() string
+ Func() reflect.Value
+
+ // Option returns the originally constructed Transformer option.
+ // The == operator can be used to detect the exact option used.
+ Option() Option
+
+ isTransform()
+ }
+)
+
+func (pa *Path) push(s PathStep) {
+ *pa = append(*pa, s)
+}
+
+func (pa *Path) pop() {
+ *pa = (*pa)[:len(*pa)-1]
+}
+
+// Last returns the last PathStep in the Path.
+// If the path is empty, this returns a non-nil PathStep that reports a nil Type.
+func (pa Path) Last() PathStep {
+ return pa.Index(-1)
+}
+
+// Index returns the ith step in the Path and supports negative indexing.
+// A negative index starts counting from the tail of the Path such that -1
+// refers to the last step, -2 refers to the second-to-last step, and so on.
+// If index is invalid, this returns a non-nil PathStep that reports a nil Type.
+func (pa Path) Index(i int) PathStep {
+ if i < 0 {
+ i = len(pa) + i
+ }
+ if i < 0 || i >= len(pa) {
+ return pathStep{}
+ }
+ return pa[i]
+}
+
+// String returns the simplified path to a node.
+// The simplified path only contains struct field accesses.
+//
+// For example:
+// MyMap.MySlices.MyField
+func (pa Path) String() string {
+ var ss []string
+ for _, s := range pa {
+ if _, ok := s.(*structField); ok {
+ ss = append(ss, s.String())
+ }
+ }
+ return strings.TrimPrefix(strings.Join(ss, ""), ".")
+}
+
+// GoString returns the path to a specific node using Go syntax.
+//
+// For example:
+// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField
+func (pa Path) GoString() string {
+ var ssPre, ssPost []string
+ var numIndirect int
+ for i, s := range pa {
+ var nextStep PathStep
+ if i+1 < len(pa) {
+ nextStep = pa[i+1]
+ }
+ switch s := s.(type) {
+ case *indirect:
+ numIndirect++
+ pPre, pPost := "(", ")"
+ switch nextStep.(type) {
+ case *indirect:
+ continue // Next step is indirection, so let them batch up
+ case *structField:
+ numIndirect-- // Automatic indirection on struct fields
+ case nil:
+ pPre, pPost = "", "" // Last step; no need for parenthesis
+ }
+ if numIndirect > 0 {
+ ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect))
+ ssPost = append(ssPost, pPost)
+ }
+ numIndirect = 0
+ continue
+ case *transform:
+ ssPre = append(ssPre, s.trans.name+"(")
+ ssPost = append(ssPost, ")")
+ continue
+ case *typeAssertion:
+ // As a special-case, elide type assertions on anonymous types
+ // since they are typically generated dynamically and can be very
+ // verbose. For example, some transforms return interface{} because
+ // of Go's lack of generics, but typically take in and return the
+ // exact same concrete type.
+ if s.Type().PkgPath() == "" {
+ continue
+ }
+ }
+ ssPost = append(ssPost, s.String())
+ }
+ for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 {
+ ssPre[i], ssPre[j] = ssPre[j], ssPre[i]
+ }
+ return strings.Join(ssPre, "") + strings.Join(ssPost, "")
+}
+
+type (
+ pathStep struct {
+ typ reflect.Type
+ }
+
+ sliceIndex struct {
+ pathStep
+ xkey, ykey int
+ }
+ mapIndex struct {
+ pathStep
+ key reflect.Value
+ }
+ typeAssertion struct {
+ pathStep
+ }
+ structField struct {
+ pathStep
+ name string
+ idx int
+
+ // These fields are used for forcibly accessing an unexported field.
+ // pvx, pvy, and field are only valid if unexported is true.
+ unexported bool
+ force bool // Forcibly allow visibility
+ pvx, pvy reflect.Value // Parent values
+ field reflect.StructField // Field information
+ }
+ indirect struct {
+ pathStep
+ }
+ transform struct {
+ pathStep
+ trans *transformer
+ }
+)
+
+func (ps pathStep) Type() reflect.Type { return ps.typ }
+func (ps pathStep) String() string {
+ if ps.typ == nil {
+ return "<nil>"
+ }
+ s := ps.typ.String()
+ if s == "" || strings.ContainsAny(s, "{}\n") {
+ return "root" // Type too simple or complex to print
+ }
+ return fmt.Sprintf("{%s}", s)
+}
+
+func (si sliceIndex) String() string {
+ switch {
+ case si.xkey == si.ykey:
+ return fmt.Sprintf("[%d]", si.xkey)
+ case si.ykey == -1:
+ // [5->?] means "I don't know where X[5] went"
+ return fmt.Sprintf("[%d->?]", si.xkey)
+ case si.xkey == -1:
+ // [?->3] means "I don't know where Y[3] came from"
+ return fmt.Sprintf("[?->%d]", si.ykey)
+ default:
+ // [5->3] means "X[5] moved to Y[3]"
+ return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey)
+ }
+}
+func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
+func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) }
+func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) }
+func (in indirect) String() string { return "*" }
+func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
+
+func (si sliceIndex) Key() int {
+ if si.xkey != si.ykey {
+ return -1
+ }
+ return si.xkey
+}
+func (si sliceIndex) SplitKeys() (x, y int) { return si.xkey, si.ykey }
+func (mi mapIndex) Key() reflect.Value { return mi.key }
+func (sf structField) Name() string { return sf.name }
+func (sf structField) Index() int { return sf.idx }
+func (tf transform) Name() string { return tf.trans.name }
+func (tf transform) Func() reflect.Value { return tf.trans.fnc }
+func (tf transform) Option() Option { return tf.trans }
+
+func (pathStep) isPathStep() {}
+func (sliceIndex) isSliceIndex() {}
+func (mapIndex) isMapIndex() {}
+func (typeAssertion) isTypeAssertion() {}
+func (structField) isStructField() {}
+func (indirect) isIndirect() {}
+func (transform) isTransform() {}
+
+var (
+ _ SliceIndex = sliceIndex{}
+ _ MapIndex = mapIndex{}
+ _ TypeAssertion = typeAssertion{}
+ _ StructField = structField{}
+ _ Indirect = indirect{}
+ _ Transform = transform{}
+
+ _ PathStep = sliceIndex{}
+ _ PathStep = mapIndex{}
+ _ PathStep = typeAssertion{}
+ _ PathStep = structField{}
+ _ PathStep = indirect{}
+ _ PathStep = transform{}
+)
+
+// isExported reports whether the identifier is exported.
+func isExported(id string) bool {
+ r, _ := utf8.DecodeRuneInString(id)
+ return unicode.IsUpper(r)
+}
+
+// isValid reports whether the identifier is valid.
+// Empty and underscore-only strings are not valid.
+func isValid(id string) bool {
+ ok := id != "" && id != "_"
+ for j, c := range id {
+ ok = ok && (j > 0 || !unicode.IsDigit(c))
+ ok = ok && (c == '_' || unicode.IsLetter(c) || unicode.IsDigit(c))
+ }
+ return ok
+}