mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-22 02:20:07 -04:00
108 lines
3.1 KiB
Go
108 lines
3.1 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package collections
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
|
|
|
"github.com/hashicorp/terraform/internal/plans"
|
|
"github.com/hashicorp/terraform/internal/plans/objchange"
|
|
)
|
|
|
|
type TransformIndices func(before, after int) computed.Diff
|
|
type ProcessIndices func(before, after int)
|
|
type IsObjType[Input any] func(input Input) bool
|
|
|
|
func TransformSlice[Input any](before, after []Input, process TransformIndices, isObjType IsObjType[Input]) ([]computed.Diff, plans.Action) {
|
|
current := plans.NoOp
|
|
if before != nil && after == nil {
|
|
current = plans.Delete
|
|
}
|
|
if before == nil && after != nil {
|
|
current = plans.Create
|
|
}
|
|
|
|
var elements []computed.Diff
|
|
ProcessSlice(before, after, func(before, after int) {
|
|
element := process(before, after)
|
|
elements = append(elements, element)
|
|
current = CompareActions(current, element.Action)
|
|
}, isObjType)
|
|
return elements, current
|
|
}
|
|
|
|
func ProcessSlice[Input any](before, after []Input, process ProcessIndices, isObjType IsObjType[Input]) {
|
|
// If before and after are the same length and is not a reordering
|
|
// we want to compare elements on an individual basis
|
|
if len(before) == len(after) && !isReorder(before, after) {
|
|
for ix := range before {
|
|
process(ix, ix)
|
|
}
|
|
return
|
|
}
|
|
|
|
lcs := objchange.LongestCommonSubsequence(before, after, func(before, after Input) bool {
|
|
return reflect.DeepEqual(before, after)
|
|
})
|
|
|
|
var beforeIx, afterIx, lcsIx int
|
|
for beforeIx < len(before) || afterIx < len(after) || lcsIx < len(lcs) {
|
|
// Step through all the before values until we hit the next item in the
|
|
// longest common subsequence. We are going to just say that all of
|
|
// these have been deleted.
|
|
for beforeIx < len(before) && (lcsIx >= len(lcs) || !reflect.DeepEqual(before[beforeIx], lcs[lcsIx])) {
|
|
isObjectDiff := isObjType(before[beforeIx]) && afterIx < len(after) && isObjType(after[afterIx]) && (lcsIx >= len(lcs) || !reflect.DeepEqual(after[afterIx], lcs[lcsIx]))
|
|
if isObjectDiff {
|
|
process(beforeIx, afterIx)
|
|
beforeIx++
|
|
afterIx++
|
|
continue
|
|
}
|
|
|
|
process(beforeIx, len(after))
|
|
beforeIx++
|
|
}
|
|
|
|
// Now, step through all the after values until hit the next item in the
|
|
// LCS. We are going to say that all of these have been created.
|
|
for afterIx < len(after) && (lcsIx >= len(lcs) || !reflect.DeepEqual(after[afterIx], lcs[lcsIx])) {
|
|
process(len(before), afterIx)
|
|
afterIx++
|
|
}
|
|
|
|
// Finally, add the item in common as unchanged.
|
|
if lcsIx < len(lcs) {
|
|
process(beforeIx, afterIx)
|
|
beforeIx++
|
|
afterIx++
|
|
lcsIx++
|
|
}
|
|
}
|
|
}
|
|
|
|
// isReorder returns true if every item of before can be found in after
|
|
func isReorder[Input any](before, after []Input) bool {
|
|
// To be a reorder the length needs to be the same
|
|
if len(before) != len(after) {
|
|
return false
|
|
}
|
|
|
|
for _, b := range before {
|
|
hasMatch := false
|
|
for _, a := range after {
|
|
if reflect.DeepEqual(b, a) {
|
|
// Match found, no need to search anymore
|
|
hasMatch = true
|
|
break
|
|
}
|
|
}
|
|
if !hasMatch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|