github.com/opentofu/opentofu@v1.7.1/internal/command/jsonformat/collections/slice.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package collections 7 8 import ( 9 "reflect" 10 11 "github.com/opentofu/opentofu/internal/command/jsonformat/computed" 12 13 "github.com/opentofu/opentofu/internal/plans" 14 "github.com/opentofu/opentofu/internal/plans/objchange" 15 ) 16 17 type TransformIndices func(before, after int) computed.Diff 18 type ProcessIndices func(before, after int) 19 type IsObjType[Input any] func(input Input) bool 20 21 func TransformSlice[Input any](before, after []Input, process TransformIndices, isObjType IsObjType[Input]) ([]computed.Diff, plans.Action) { 22 current := plans.NoOp 23 if before != nil && after == nil { 24 current = plans.Delete 25 } 26 if before == nil && after != nil { 27 current = plans.Create 28 } 29 30 var elements []computed.Diff 31 ProcessSlice(before, after, func(before, after int) { 32 element := process(before, after) 33 elements = append(elements, element) 34 current = CompareActions(current, element.Action) 35 }, isObjType) 36 return elements, current 37 } 38 39 func ProcessSlice[Input any](before, after []Input, process ProcessIndices, isObjType IsObjType[Input]) { 40 lcs := objchange.LongestCommonSubsequence(before, after, func(before, after Input) bool { 41 return reflect.DeepEqual(before, after) 42 }) 43 44 var beforeIx, afterIx, lcsIx int 45 for beforeIx < len(before) || afterIx < len(after) || lcsIx < len(lcs) { 46 // Step through all the before values until we hit the next item in the 47 // longest common subsequence. We are going to just say that all of 48 // these have been deleted. 49 for beforeIx < len(before) && (lcsIx >= len(lcs) || !reflect.DeepEqual(before[beforeIx], lcs[lcsIx])) { 50 isObjectDiff := isObjType(before[beforeIx]) && afterIx < len(after) && isObjType(after[afterIx]) && (lcsIx >= len(lcs) || !reflect.DeepEqual(after[afterIx], lcs[lcsIx])) 51 if isObjectDiff { 52 process(beforeIx, afterIx) 53 beforeIx++ 54 afterIx++ 55 continue 56 } 57 58 process(beforeIx, len(after)) 59 beforeIx++ 60 } 61 62 // Now, step through all the after values until hit the next item in the 63 // LCS. We are going to say that all of these have been created. 64 for afterIx < len(after) && (lcsIx >= len(lcs) || !reflect.DeepEqual(after[afterIx], lcs[lcsIx])) { 65 process(len(before), afterIx) 66 afterIx++ 67 } 68 69 // Finally, add the item in common as unchanged. 70 if lcsIx < len(lcs) { 71 process(beforeIx, afterIx) 72 beforeIx++ 73 afterIx++ 74 lcsIx++ 75 } 76 } 77 }