github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/jsonformat/collections/slice.go (about)

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