114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
|
package diff
|
||
|
|
||
|
import "github.com/zclconf/go-cty/cty"
|
||
|
|
||
|
// diffListsShallow takes two values that must be known, non-null lists of the
|
||
|
// same element type and returns a shallow diff that adds and removes only
|
||
|
// direct list members, even if they are themselves collection- or
|
||
|
// structural-typed values.
|
||
|
func diffListsShallow(old cty.Value, new cty.Value, path cty.Path) Diff {
|
||
|
var diff Diff
|
||
|
|
||
|
oldEls := make([]cty.Value, 0, old.LengthInt())
|
||
|
newEls := make([]cty.Value, 0, old.LengthInt())
|
||
|
it := old.ElementIterator()
|
||
|
for it.Next() {
|
||
|
_, v := it.Element()
|
||
|
oldEls = append(oldEls, v)
|
||
|
}
|
||
|
it = new.ElementIterator()
|
||
|
for it.Next() {
|
||
|
_, v := it.Element()
|
||
|
newEls = append(newEls, v)
|
||
|
}
|
||
|
|
||
|
lcs := longestCommonSubsequence(oldEls, newEls)
|
||
|
op := 0 // position in "old"
|
||
|
np := 0 // position in "new"
|
||
|
cp := 0 // position in "lcs"
|
||
|
ip := int64(0) // current index for diff changes
|
||
|
|
||
|
path = append(path, nil)
|
||
|
step := &path[len(path)-1]
|
||
|
|
||
|
for op < len(oldEls) || np < len(newEls) {
|
||
|
|
||
|
// We want to produce blocks of removes, adds, and contexts
|
||
|
// until we run out of items.
|
||
|
|
||
|
// Elements unique to old are deleted
|
||
|
for op < len(oldEls) {
|
||
|
if cp < len(lcs) {
|
||
|
eq := oldEls[op].Equals(lcs[cp])
|
||
|
if eq.IsKnown() && eq.True() {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
*step = cty.IndexStep{
|
||
|
Key: cty.NumberIntVal(ip),
|
||
|
}
|
||
|
diff = append(diff, DeleteChange{
|
||
|
Path: path.Copy(),
|
||
|
OldValue: oldEls[op],
|
||
|
})
|
||
|
op++
|
||
|
}
|
||
|
|
||
|
// Elements unique to new are inserted
|
||
|
for np < len(newEls) {
|
||
|
if cp < len(lcs) {
|
||
|
eq := newEls[np].Equals(lcs[cp])
|
||
|
if eq.IsKnown() && eq.True() {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
*step = cty.IndexStep{
|
||
|
Key: cty.NumberIntVal(ip),
|
||
|
}
|
||
|
var beforeVal cty.Value
|
||
|
if op < len(oldEls) {
|
||
|
beforeVal = oldEls[op]
|
||
|
} else {
|
||
|
beforeVal = cty.NullVal(old.Type().ElementType())
|
||
|
}
|
||
|
diff = append(diff, InsertChange{
|
||
|
Path: path.Copy(),
|
||
|
NewValue: newEls[np],
|
||
|
BeforeValue: beforeVal,
|
||
|
})
|
||
|
np++
|
||
|
ip++
|
||
|
}
|
||
|
|
||
|
// Elements common to both are context.
|
||
|
// For this loop we'll advance all three pointers at once because
|
||
|
// we expect to be walking through the same elements in all three.
|
||
|
for cp < len(lcs) && op < len(oldEls) && np < len(newEls) {
|
||
|
oeq := oldEls[op].Equals(lcs[cp])
|
||
|
if !(oeq.IsKnown() && oeq.True()) {
|
||
|
break
|
||
|
}
|
||
|
neq := newEls[np].Equals(lcs[cp])
|
||
|
if !(neq.IsKnown() && neq.True()) {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
*step = cty.IndexStep{
|
||
|
Key: cty.NumberIntVal(ip),
|
||
|
}
|
||
|
diff = append(diff, Context{
|
||
|
Path: path.Copy(),
|
||
|
WantValue: lcs[cp],
|
||
|
})
|
||
|
|
||
|
cp++
|
||
|
op++
|
||
|
np++
|
||
|
ip++
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return diff
|
||
|
}
|