feat(diff): диф-движок шаблон↔зона с Actionable и ReadOnly для NS/SOA

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 12:32:33 +07:00
parent 304632b8cf
commit bd4f8c5a8c
2 changed files with 154 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
package diff
import "github.com/vasyakrg/dns-autoresolver/internal/model"
type ChangeKind string
const (
InSync ChangeKind = "in_sync"
Add ChangeKind = "add"
Update ChangeKind = "update"
Delete ChangeKind = "delete"
)
// RecordDiff describes one RRset's deviation between template and zone.
type RecordDiff struct {
Kind ChangeKind
Type model.RecordType
Name string
Desired *model.Record // nil for Delete
Actual *model.Record // nil for Add
ReadOnly bool // NS/SOA — shown but never applied
}
type Changeset struct {
Diffs []RecordDiff
}
// Actionable returns managed diffs that are not in sync.
func (c Changeset) Actionable() []RecordDiff {
var out []RecordDiff
for _, d := range c.Diffs {
if d.ReadOnly || d.Kind == InSync {
continue
}
out = append(out, d)
}
return out
}
// Diff compares a template against the actual zone records.
// Records present in the zone but absent from the template yield Delete.
func Diff(template, actual []model.Record) Changeset {
current := index(actual)
seen := make(map[string]bool, len(template))
var diffs []RecordDiff
for _, t := range template {
tt := t
key := tt.Key()
seen[key] = true
ro := !tt.Type.Managed()
if a, ok := current[key]; ok {
ac := a
kind := Update
if tt.Equal(ac) {
kind = InSync
}
diffs = append(diffs, RecordDiff{Kind: kind, Type: tt.Type, Name: tt.Name, Desired: &tt, Actual: &ac, ReadOnly: ro})
} else {
diffs = append(diffs, RecordDiff{Kind: Add, Type: tt.Type, Name: tt.Name, Desired: &tt, ReadOnly: ro})
}
}
for _, a := range actual {
ac := a
if seen[ac.Key()] {
continue
}
diffs = append(diffs, RecordDiff{Kind: Delete, Type: ac.Type, Name: ac.Name, Actual: &ac, ReadOnly: !ac.Type.Managed()})
}
return Changeset{Diffs: diffs}
}
func index(recs []model.Record) map[string]model.Record {
m := make(map[string]model.Record, len(recs))
for _, r := range recs {
m[r.Key()] = r
}
return m
}