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  }