From b4328edd73ed40c251d82a7067c5d96fd112724b Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Thu, 22 Aug 2024 19:13:00 -0400 Subject: [PATCH] Add config.File.Diff to diff two config files --- config/file.go | 37 +++++++++++++++++++++++++++++++++++- config/file_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/config/file.go b/config/file.go index a99a4a1..2fc10af 100644 --- a/config/file.go +++ b/config/file.go @@ -143,6 +143,14 @@ func ParseValue (str string) (Value, error) { } } +// Has returns whether the key exists. If the key is invalid, it returns false, +// ErrMalformedKey. +func (this *File) Has (key string) (bool, error) { + if !KeyValid(key) { return false, ErrMalformedKey } + _, ok := this.keys[key] + return ok, nil +} + // Get gets the keyed value. If the value is unspecified, it returns nil, // ErrNonexistentEntry. If the key is invalid, it returns nil, ErrMalformedKey. func (this *File) Get (key string) (Value, error) { @@ -186,7 +194,7 @@ func (this *File) Reset (key string) error { return nil } -// Map returns a map of keys to values. +// Map creates and returns a map of keys to values. func (this *File) Map () map[string] Value { mp := make(map[string] Value) for key, index := range this.keys { @@ -197,6 +205,33 @@ func (this *File) Map () map[string] Value { return mp } +// Diff returns a set of keys that are different from the other file. +func (this *File) Diff (other *File) map[string] struct { } { + diff := make(map[string] struct { }) + + // - keys only we have + // - keys we both have, but are different + for key, index := range this.keys { + otherIndex, ok := other.keys[key] + if !ok { + diff[key] = struct { } { } + continue + } + if !this.lines[index].(entry).value.Equals(other.lines[otherIndex].(entry).value) { + diff[key] = struct { } { } + } + } + + // - keys only they have + for key := range other.keys { + if _, has := this.keys[key]; !has { + diff[key] = struct { } { } + } + } + + return diff +} + // WriteTo writes the data in this file to an io.Writer. func (file *File) WriteTo (writer io.Writer) (n int64, err error) { for _, lin := range file.lines { diff --git a/config/file_test.go b/config/file_test.go index 0d081c4..8a696c7 100644 --- a/config/file_test.go +++ b/config/file_test.go @@ -1,6 +1,7 @@ package config import "math" +import "maps" import "strings" import "testing" @@ -103,6 +104,51 @@ key1=7 `) } +func TestDiffNone (test *testing.T) { + str := ` +thing = something + +otherThing = otherValue +# comment +otherThing = true +otherThing = 234 + +yetAnotherThing = 0.23498 +` + file1 := parseFileString(test, str) + file2 := parseFileString(test, str) + diff := file1.Diff(file2) + if len(diff) != 0 { + test.Fatalf("diff not empty:\n%v", diff) + } +} + +func TestDiff (test *testing.T) { + file1 := parseFileString(test, +`key4=0 +key1=value1 +# comment +key2=34`) + file2 := parseFileString(test, +`key1=value2 +key2=34 +# comment + +key3=0.2`) + diff := file1.Diff(file2) + correct := map[string] struct { } { + "key1": struct { } { }, + "key3": struct { } { }, + "key4": struct { } { }, + } + if !maps.Equal(diff, correct) { + test.Error("diffs do not match") + test.Errorf("EXPECTED:\n%v", correct) + test.Errorf("GOT:\n%v", diff) + test.Fail() + } +} + func testParseEntry (test *testing.T, str string, key string, value Value) { ent, err := parseEntry(str) if err != nil { test.Fatal(err) }