From 2f12b4dd0aa2da6671c683f2f3d6a60009e0d300 Mon Sep 17 00:00:00 2001 From: sh0rez Date: Tue, 30 Jun 2020 18:14:04 +0200 Subject: [PATCH] refactor: expose the DefaultReporter I am trying to stop go-cmp from calling fmt.Stringer, as this in some cases makes the output worse instead of better. The DefaultReporter implementation is quite customizable, but this functionality is not exposed to the user. While users can register custom exporters, they would then need to re-implement the entire DefaultExporter, even though changing a single bool in it's config would suffice. This PR exposes the DefaultReporter implementation so it can be customized and used outside of this package --- cmp/compare.go | 2 +- cmp/options_test.go | 8 ++++---- cmp/report.go | 21 +++++++++++---------- cmp/report_compare.go | 18 +++++++++--------- cmp/report_reflect.go | 8 ++++---- cmp/report_slices.go | 6 +++--- 6 files changed, 32 insertions(+), 31 deletions(-) diff --git a/cmp/compare.go b/cmp/compare.go index 580ae20..af2f4d3 100644 --- a/cmp/compare.go +++ b/cmp/compare.go @@ -123,7 +123,7 @@ func Diff(x, y interface{}, opts ...Option) string { s.result = diff.Result{} // Reset results } - r := new(defaultReporter) + r := new(DefaultReporter) s.reporters = append(s.reporters, reporter{r}) s.compareAny(rootStep(x, y)) d := r.String() diff --git a/cmp/options_test.go b/cmp/options_test.go index f8066c7..bfc83d7 100644 --- a/cmp/options_test.go +++ b/cmp/options_test.go @@ -128,7 +128,7 @@ func TestOptionPanic(t *testing.T) { }, { label: "FilterPath", fnc: FilterPath, - args: []interface{}{func(Path) bool { return true }, Reporter(&defaultReporter{})}, + args: []interface{}{func(Path) bool { return true }, Reporter(&DefaultReporter{})}, wantPanic: "invalid option type", }, { label: "FilterPath", @@ -137,7 +137,7 @@ func TestOptionPanic(t *testing.T) { }, { label: "FilterPath", fnc: FilterPath, - args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}}, + args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Reporter(&DefaultReporter{})}}, wantPanic: "invalid option type", }, { label: "FilterValues", @@ -170,7 +170,7 @@ func TestOptionPanic(t *testing.T) { }, { label: "FilterValues", fnc: FilterValues, - args: []interface{}{func(int, int) bool { return true }, Reporter(&defaultReporter{})}, + args: []interface{}{func(int, int) bool { return true }, Reporter(&DefaultReporter{})}, wantPanic: "invalid option type", }, { label: "FilterValues", @@ -179,7 +179,7 @@ func TestOptionPanic(t *testing.T) { }, { label: "FilterValues", fnc: FilterValues, - args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}}, + args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Reporter(&DefaultReporter{})}}, wantPanic: "invalid option type", }} diff --git a/cmp/report.go b/cmp/report.go index aafcb36..ab1eb3f 100644 --- a/cmp/report.go +++ b/cmp/report.go @@ -4,10 +4,10 @@ package cmp -// defaultReporter implements the reporter interface. +// DefaultReporter implements the reporter interface. // // As Equal serially calls the PushStep, Report, and PopStep methods, the -// defaultReporter constructs a tree-based representation of the compared value +// DefaultReporter constructs a tree-based representation of the compared value // and the result of each comparison (see valueNode). // // When the String method is called, the FormatDiff method transforms the @@ -15,34 +15,35 @@ package cmp // of the textual output (see textNode). // // Lastly, the textNode.String method produces the final report as a string. -type defaultReporter struct { - root *valueNode - curr *valueNode +type DefaultReporter struct { + root *valueNode + curr *valueNode + FormatOptions FormatOptions } -func (r *defaultReporter) PushStep(ps PathStep) { +func (r *DefaultReporter) PushStep(ps PathStep) { r.curr = r.curr.PushStep(ps) if r.root == nil { r.root = r.curr } } -func (r *defaultReporter) Report(rs Result) { +func (r *DefaultReporter) Report(rs Result) { r.curr.Report(rs) } -func (r *defaultReporter) PopStep() { +func (r *DefaultReporter) PopStep() { r.curr = r.curr.PopStep() } // String provides a full report of the differences detected as a structured // literal in pseudo-Go syntax. String may only be called after the entire tree // has been traversed. -func (r *defaultReporter) String() string { +func (r *DefaultReporter) String() string { assert(r.root != nil && r.curr == nil) if r.root.NumDiff == 0 { return "" } ptrs := new(pointerReferences) - text := formatOptions{}.FormatDiff(r.root, ptrs) + text := r.FormatOptions.FormatDiff(r.root, ptrs) resolveReferences(text) return text.String() } diff --git a/cmp/report_compare.go b/cmp/report_compare.go index 9e21809..a44efaa 100644 --- a/cmp/report_compare.go +++ b/cmp/report_compare.go @@ -35,7 +35,7 @@ const ( autoType ) -type formatOptions struct { +type FormatOptions struct { // DiffMode controls the output mode of FormatDiff. // // If diffUnknown, then produce a diff of the x and y values. @@ -52,23 +52,23 @@ type formatOptions struct { TypeMode typeMode // formatValueOptions are options specific to printing reflect.Values. - formatValueOptions + FormatValueOptions } -func (opts formatOptions) WithDiffMode(d diffMode) formatOptions { +func (opts FormatOptions) WithDiffMode(d diffMode) FormatOptions { opts.DiffMode = d return opts } -func (opts formatOptions) WithTypeMode(t typeMode) formatOptions { +func (opts FormatOptions) WithTypeMode(t typeMode) FormatOptions { opts.TypeMode = t return opts } -func (opts formatOptions) WithVerbosity(level int) formatOptions { +func (opts FormatOptions) WithVerbosity(level int) FormatOptions { opts.VerbosityLevel = level opts.LimitVerbosity = true return opts } -func (opts formatOptions) verbosity() uint { +func (opts FormatOptions) verbosity() uint { switch { case opts.VerbosityLevel < 0: return 0 @@ -83,7 +83,7 @@ const maxVerbosityPreset = 3 // verbosityPreset modifies the verbosity settings given an index // between 0 and maxVerbosityPreset, inclusive. -func verbosityPreset(opts formatOptions, i int) formatOptions { +func verbosityPreset(opts FormatOptions, i int) FormatOptions { opts.VerbosityLevel = int(opts.verbosity()) + 2*i if i > 0 { opts.AvoidStringer = true @@ -97,7 +97,7 @@ func verbosityPreset(opts formatOptions, i int) formatOptions { // 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, ptrs *pointerReferences) (out textNode) { +func (opts FormatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) { if opts.DiffMode == diffIdentical { opts = opts.WithVerbosity(1) } else { @@ -200,7 +200,7 @@ func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out } } -func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) 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 diff --git a/cmp/report_reflect.go b/cmp/report_reflect.go index 28e0e92..45b4c97 100644 --- a/cmp/report_reflect.go +++ b/cmp/report_reflect.go @@ -15,7 +15,7 @@ import ( "github.com/google/go-cmp/cmp/internal/value" ) -type formatValueOptions struct { +type FormatValueOptions struct { // AvoidStringer controls whether to avoid calling custom stringer // methods like error.Error or fmt.Stringer.String. AvoidStringer bool @@ -40,7 +40,7 @@ type formatValueOptions struct { // FormatType prints the type as if it were wrapping s. // This may return s as-is depending on the current type and TypeMode mode. -func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { +func (opts FormatOptions) FormatType(t reflect.Type, s textNode) textNode { // Check whether to emit the type or not. switch opts.TypeMode { case autoType: @@ -103,7 +103,7 @@ func wrapParens(s textNode) textNode { // FormatValue prints the reflect.Value, taking extra care to avoid descending // 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) { +func (opts FormatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) { if !v.IsValid() { return nil } @@ -304,7 +304,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, // formatMapKey formats v as if it were a map key. // The result is guaranteed to be a single line. func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string { - var opts formatOptions + var opts FormatOptions opts.DiffMode = diffIdentical opts.TypeMode = elideType opts.PrintAddresses = disambiguate diff --git a/cmp/report_slices.go b/cmp/report_slices.go index 35315da..9a118dc 100644 --- a/cmp/report_slices.go +++ b/cmp/report_slices.go @@ -18,7 +18,7 @@ import ( // CanFormatDiffSlice reports whether we support custom formatting for nodes // that are slices of primitive kinds or strings. -func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { +func (opts FormatOptions) CanFormatDiffSlice(v *valueNode) bool { switch { case opts.DiffMode != diffUnknown: return false // Must be formatting in diff mode @@ -74,7 +74,7 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { // FormatDiffSlice prints a diff for the slices (or strings) represented by v. // This provides custom-tailored logic to make printing of differences in // textual strings and slices of primitive kinds more readable. -func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { +func (opts FormatOptions) FormatDiffSlice(v *valueNode) textNode { assert(opts.DiffMode == diffUnknown) t, vx, vy := v.Type, v.ValueX, v.ValueY @@ -317,7 +317,7 @@ func formatASCII(s string) string { return string(b) } -func (opts formatOptions) formatDiffSlice( +func (opts FormatOptions) formatDiffSlice( vx, vy reflect.Value, chunkSize int, name string, makeRec func(reflect.Value, diffMode) textRecord, ) (list textList) {