Traefik_Control/backend/internal/config/file/diff.go

59 lines
1.6 KiB
Go

package file
import (
"strings"
"github.com/pmezard/go-difflib/difflib"
)
// UnifiedDiff returns a proper unified diff between old and new content using LCS (Myers via difflib).
// It handles insertions, deletions, replacements, moved blocks, empty and new files.
func UnifiedDiff(filename, oldContent, newContent string) string {
if oldContent == newContent {
return ""
}
oldLines := splitLines(oldContent)
newLines := splitLines(newContent)
diff := difflib.UnifiedDiff{
A: difflib.SplitLines(oldContent),
B: difflib.SplitLines(newContent),
FromFile: "a/" + filename,
ToFile: "b/" + filename,
Context: 3,
}
// difflib.SplitLines handles trailing newlines correctly, but we also keep oldLines/newLines for
// fallback when one side is empty to ensure diff is still produced.
if len(oldLines) == 0 && len(newLines) == 0 {
return ""
}
text, _ := difflib.GetUnifiedDiffString(diff)
// difflib returns empty string when no diff; ensure header is present for new/deleted files
if text == "" {
// Fallback minimal diff for edge cases difflib might consider empty
var b strings.Builder
b.WriteString("--- a/" + filename + "\n+++ b/" + filename + "\n")
if len(oldLines) == 0 {
for _, l := range newLines {
b.WriteString("+" + l + "\n")
}
return b.String()
}
if len(newLines) == 0 {
for _, l := range oldLines {
b.WriteString("-" + l + "\n")
}
return b.String()
}
return ""
}
return text
}
func splitLines(s string) []string {
if s == "" {
return []string{}
}
s = strings.ReplaceAll(s, "\r\n", "\n")
return strings.Split(s, "\n")
}