forked from knative/pkg
/
reporters.go
136 lines (118 loc) · 3.72 KB
/
reporters.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
Copyright 2019 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kmp
import (
"fmt"
"reflect"
"sort"
"strings"
"github.com/google/go-cmp/cmp"
)
// FieldListReporter implements the cmp.Reporter interface. It keeps
// track of the field names that differ between two structs and reports
// them through the Fields() function.
type FieldListReporter struct {
path cmp.Path
fieldNames []string
}
// PushStep implements the cmp.Reporter.
func (r *FieldListReporter) PushStep(ps cmp.PathStep) {
r.path = append(r.path, ps)
}
// fieldName returns a readable name for the field. If the field has JSON annotations it
// returns the JSON key. If the field does not have JSON annotations or the JSON annotation
// marks the field as ignored it returns the field's go name
func (r *FieldListReporter) fieldName() string {
if len(r.path) < 2 {
return r.path.Index(0).String()
} else {
fieldName := strings.TrimPrefix(r.path.Index(1).String(), ".")
// Prefer JSON name to fieldName if it exists
structField, exists := r.path.Index(0).Type().FieldByName(fieldName)
if exists {
tag := structField.Tag.Get("json")
if tag != "" && tag != "-" {
return strings.SplitN(tag, ",", 2)[0]
}
}
return fieldName
}
}
// Report implements the cmp.Reporter.
func (r *FieldListReporter) Report(rs cmp.Result) {
if rs.Equal() {
return
}
name := r.fieldName()
// Only append elements we don't already have.
for _, v := range r.fieldNames {
if name == v {
return
}
}
r.fieldNames = append(r.fieldNames, name)
}
// PopStep implements cmp.Reporter.
func (r *FieldListReporter) PopStep() {
r.path = r.path[:len(r.path)-1]
}
// Fields returns the field names that differed between the two
// objects after calling cmp.Equal with the FieldListReporter. Field names
// are returned in alphabetical order.
func (r *FieldListReporter) Fields() []string {
sort.Strings(r.fieldNames)
return r.fieldNames
}
// ShortDiffReporter implements the cmp.Reporter interface. It reports
// on fields which have diffing values in a short zero-context, unified diff
// format.
type ShortDiffReporter struct {
path cmp.Path
diffs []string
err error
}
// PushStep implements the cmp.Reporter.
func (r *ShortDiffReporter) PushStep(ps cmp.PathStep) {
r.path = append(r.path, ps)
}
// Report implements the cmp.Reporter.
func (r *ShortDiffReporter) Report(rs cmp.Result) {
if rs.Equal() {
return
}
cur := r.path.Last()
vx, vy := cur.Values()
t := cur.Type()
var diff string
// Prefix struct values with the types to add clarity in output
if !vx.IsValid() || !vy.IsValid() {
r.err = fmt.Errorf("Unable to diff %+v and %+v on path %#v", vx, vy, r.path)
} else if t.Kind() == reflect.Struct {
diff = fmt.Sprintf("%#v:\n\t-: %+v: \"%+v\"\n\t+: %+v: \"%+v\"\n", r.path, t, vx, t, vy)
} else {
diff = fmt.Sprintf("%#v:\n\t-: \"%+v\"\n\t+: \"%+v\"\n", r.path, vx, vy)
}
r.diffs = append(r.diffs, diff)
}
// PopStep implements the cmp.Reporter.
func (r *ShortDiffReporter) PopStep() {
r.path = r.path[:len(r.path)-1]
}
// Diff returns the generated short diff for this object.
// cmp.Equal should be called before this method.
func (r *ShortDiffReporter) Diff() (string, error) {
if r.err != nil {
return "", r.err
}
return strings.Join(r.diffs, ""), nil
}