-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiff.go
More file actions
124 lines (105 loc) · 2.75 KB
/
diff.go
File metadata and controls
124 lines (105 loc) · 2.75 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
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
package testastic
import (
"strings"
)
type diffType string
const (
// diffChanged indicates a value was modified.
diffChanged diffType = "changed"
// diffAdded indicates a value was added (exists in actual but not expected).
diffAdded diffType = "added"
// diffRemoved indicates a value was removed (exists in expected but not actual).
diffRemoved diffType = "removed"
// diffTypeMismatch indicates the types don't match.
diffTypeMismatch diffType = "type mismatch"
// diffMatcherFailed indicates a matcher didn't match the actual value.
diffMatcherFailed diffType = "matcher failed"
)
func (d diffType) String() string {
return string(d)
}
// difference represents a single difference between expected and actual JSON.
type difference struct {
Path string // JSON path, e.g., "$.users[0].name"
Expected any // Expected value (or matcher description)
Actual any // Actual value
Type diffType // Type of difference
}
func formatFileDiffInline(expected, actual []string) string {
diff := computeDiff(expected, actual)
var sb strings.Builder
for _, line := range diff {
sb.WriteString(line)
sb.WriteString("\n")
}
return sb.String()
}
type diffOp string
const (
diffEqual diffOp = "equal"
diffDelete diffOp = "delete"
diffInsert diffOp = "insert"
)
// computeDiff generates a unified diff between two sets of lines.
// Uses a simple LCS-based algorithm for readability.
//
//nolint:funlen // LCS algorithm requires sequential steps.
func computeDiff(expected, actual []string) []string {
// Build the longest common subsequence (LCS) matrix.
m, n := len(expected), len(actual)
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
if expected[i-1] == actual[j-1] {
dp[i][j] = dp[i-1][j-1] + 1
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
}
}
}
// Backtrack through LCS matrix to build diff operations.
var result []string
i, j := m, n
var ops []struct {
op diffOp
line string
}
for i > 0 || j > 0 {
switch {
case i > 0 && j > 0 && expected[i-1] == actual[j-1]:
ops = append(ops, struct {
op diffOp
line string
}{diffEqual, expected[i-1]})
i--
j--
case j > 0 && (i == 0 || dp[i][j-1] >= dp[i-1][j]):
ops = append(ops, struct {
op diffOp
line string
}{diffInsert, actual[j-1]})
j--
case i > 0:
ops = append(ops, struct {
op diffOp
line string
}{diffDelete, expected[i-1]})
i--
}
}
for k := len(ops) - 1; k >= 0; k-- {
op := ops[k]
switch op.op {
case diffEqual:
result = append(result, " "+op.line)
case diffDelete:
result = append(result, red("- "+op.line))
case diffInsert:
result = append(result, green("+ "+op.line))
}
}
return result
}