diff --git a/config.go b/config.go index c91d0ef..a6fa334 100644 --- a/config.go +++ b/config.go @@ -10,9 +10,10 @@ import ( type config struct { diff.Output - ignore patterns - lhsFile string - rhsFile string + ignore patterns + lhsFile string + rhsFile string + outputReport bool } func readConfig() config { @@ -20,6 +21,7 @@ func readConfig() config { flag.StringVar(&c.Output.Indent, "indent", " ", "indent string") flag.BoolVar(&c.Output.ShowTypes, "show-types", false, "show types") + flag.BoolVar(&c.outputReport, "report", false, "output report format") flag.Var(&c.ignore, "ignore", "paths to ignore (glob)") flag.Parse() diff --git a/diff/diff_test.go b/diff/diff_test.go index 0e7cd1e..0ad2bc9 100644 --- a/diff/diff_test.go +++ b/diff/diff_test.go @@ -404,6 +404,46 @@ func TestIgnore(t *testing.T) { } } +func TestReport(t *testing.T) { + want := []string{ + "content", + "type", + } + + d, err := Diff( + map[string]interface{}{ + "match": 5, + "content": 6, + "type": 8, + }, + map[string]interface{}{ + "match": 5, + "content": 7, + "type": 9.0, + }, + ) + if err != nil { + t.Errorf("Diff(...): unexpected error: %s", err) + return + } + errs, err := Report(d, testOutput) + if err != nil { + t.Errorf("Report(Diff(...), %+v): unexpected error: %s", err, testOutput) + return + } + + if len(errs) != len(want) { + t.Errorf("len(Report(Diff(...), %+v)) = %d, expected %d", testOutput, len(errs), len(want)) + return + } + + for i, e := range errs { + if !strings.Contains(e.Error(), want[i]) { + t.Errorf("Report(Diff(...), %+v)[%d] = %q, should contain %q", testOutput, i, e.Error(), want[i]) + } + } +} + func testStrings(context string, t *testing.T, test stringTest, ss []string, indented string) { for i, want := range test.Want { s := ss[i] diff --git a/diff/map.go b/diff/map.go index d81fa5e..ac0feb8 100644 --- a/diff/map.go +++ b/diff/map.go @@ -180,7 +180,18 @@ func (m Map) StringIndent(keyprefix, prefix string, conf Output) string { } func (m Map) Walk(path string, fn WalkFn) error { - for k, diff := range m.Diffs { + var keys []interface{} + + for k := range m.Diffs { + keys = append(keys, k) + } + + sort.Slice(keys, func(i, j int) bool { + return strings.Compare(fmt.Sprintf("%v", keys[i]), fmt.Sprintf("%v", keys[j])) == -1 + }) + + for _, k := range keys { + diff := m.Diffs[k] err := walk(m, diff, fmt.Sprintf("%s.%v", path, k), fn) if err != nil { return err diff --git a/diff/report.go b/diff/report.go new file mode 100644 index 0000000..20b7537 --- /dev/null +++ b/diff/report.go @@ -0,0 +1,29 @@ +package diff + +type ReportError string + +func (e ReportError) Error() string { + return string(e) +} + +func Report(d Differ, outConf Output) ([]error, error) { + var errs []error + + err := Walk(d, func(parent, diff Differ, path string) error { + switch diff.Diff() { + case Identical: + return nil + case TypesDiffer: + errs = append(errs, ReportError(diff.StringIndent(" "+path+": ", "", outConf))) + case ContentDiffer: + if _, ok := diff.(Walker); ok { + return nil + } + errs = append(errs, ReportError(diff.StringIndent(" "+path+": ", "", outConf))) + } + + return nil + }) + + return errs, err +} diff --git a/main.go b/main.go index e624210..7d551a5 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,18 @@ func main() { err = pruneIgnore(d, conf.ignore) - fmt.Println(d.StringIndent("", "", conf.Output)) + if conf.outputReport { + errs, err := diff.Report(d, conf.Output) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: Failed to generate report: %s", err) + os.Exit(StatusDiffError) + } + for _, e := range errs { + fmt.Println(e.Error()) + } + } else { + fmt.Println(d.StringIndent("", "", conf.Output)) + } if d.Diff() != diff.Identical { os.Exit(StatusDiffMismatch) }