-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiffx.go
More file actions
91 lines (83 loc) · 3.03 KB
/
diffx.go
File metadata and controls
91 lines (83 loc) · 3.03 KB
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
package diffx
import (
"bytes"
"encoding/json"
"fmt"
"io"
)
// Diff computes a unified diff between before and after and returns the
// formatted result as a string. Color defaults to false since the output
// is a string (use WithColor(true) to force ANSI codes).
func Diff(before, after string, opts ...ReporterOption) string {
var buf bytes.Buffer
r := newReporterWithDefaults(opts, false)
// Error is only from writing to a bytes.Buffer, which never fails.
_ = r.Report(&buf, "before", "after", before, after)
return buf.String()
}
// WriteDiff computes a unified diff between before and after and writes
// the formatted result to w. Color is auto-detected when w is an *os.File
// pointing to a TTY (use WithColor to override).
func WriteDiff(w io.Writer, before, after string, opts ...ReporterOption) error {
r := newReporterWithDefaults(opts, true)
return r.Report(w, "before", "after", before, after)
}
// JSONDiff computes a unified diff between two JSON values. Both inputs are
// normalized (unmarshaled and re-marshaled with sorted keys and consistent
// indentation) before diffing, making the output stable regardless of
// original key ordering. Returns the formatted diff as a string.
// Color defaults to false.
func JSONDiff(before, after string, opts ...ReporterOption) (string, error) {
normBefore, err := normalizeJSON(before)
if err != nil {
return "", fmt.Errorf("normalizing before JSON: %w", err)
}
normAfter, err := normalizeJSON(after)
if err != nil {
return "", fmt.Errorf("normalizing after JSON: %w", err)
}
return Diff(normBefore, normAfter, opts...), nil
}
// WriteJSONDiff computes a unified diff between two JSON values and writes
// it to w. Both inputs are normalized before diffing. Color is auto-detected
// when w is an *os.File pointing to a TTY.
func WriteJSONDiff(w io.Writer, before, after string, opts ...ReporterOption) error {
normBefore, err := normalizeJSON(before)
if err != nil {
return fmt.Errorf("normalizing before JSON: %w", err)
}
normAfter, err := normalizeJSON(after)
if err != nil {
return fmt.Errorf("normalizing after JSON: %w", err)
}
return WriteDiff(w, normBefore, normAfter, opts...)
}
// newReporterWithDefaults creates a reporter, applying a color default if the
// caller hasn't explicitly set one via opts.
func newReporterWithDefaults(opts []ReporterOption, autoDetectColor bool) *UnifiedDiffReporter {
cfg := defaultConfig()
if !autoDetectColor {
colorOff := false
cfg.color = &colorOff
}
for _, o := range opts {
o(&cfg)
}
return &UnifiedDiffReporter{cfg: cfg}
}
// normalizeJSON unmarshals JSON into an ordered structure and re-marshals it
// with sorted keys and 2-space indentation. This ensures a stable, diffable
// representation regardless of original key order.
func normalizeJSON(s string) (string, error) {
var v any
dec := json.NewDecoder(bytes.NewReader([]byte(s)))
dec.UseNumber()
if err := dec.Decode(&v); err != nil {
return "", err
}
out, err := json.MarshalIndent(v, "", " ")
if err != nil {
return "", err
}
return string(out) + "\n", nil
}