github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonformat/differ/set.go (about)

     1  package differ
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/zclconf/go-cty/cty"
     7  
     8  	"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
     9  	"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
    10  	"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
    11  	"github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path"
    12  	"github.com/hashicorp/terraform/internal/command/jsonprovider"
    13  	"github.com/hashicorp/terraform/internal/plans"
    14  )
    15  
    16  func (change Change) computeAttributeDiffAsSet(elementType cty.Type) computed.Diff {
    17  	var elements []computed.Diff
    18  	current := change.getDefaultActionForIteration()
    19  	change.processSet(func(value Change) {
    20  		element := value.ComputeDiffForType(elementType)
    21  		elements = append(elements, element)
    22  		current = collections.CompareActions(current, element.Action)
    23  	})
    24  	return computed.NewDiff(renderers.Set(elements), current, change.ReplacePaths.Matches())
    25  }
    26  
    27  func (change Change) computeAttributeDiffAsNestedSet(attributes map[string]*jsonprovider.Attribute) computed.Diff {
    28  	var elements []computed.Diff
    29  	current := change.getDefaultActionForIteration()
    30  	change.processSet(func(value Change) {
    31  		element := value.computeDiffForNestedAttribute(&jsonprovider.NestedType{
    32  			Attributes:  attributes,
    33  			NestingMode: "single",
    34  		})
    35  		elements = append(elements, element)
    36  		current = collections.CompareActions(current, element.Action)
    37  	})
    38  	return computed.NewDiff(renderers.NestedSet(elements), current, change.ReplacePaths.Matches())
    39  }
    40  
    41  func (change Change) computeBlockDiffsAsSet(block *jsonprovider.Block) ([]computed.Diff, plans.Action) {
    42  	var elements []computed.Diff
    43  	current := change.getDefaultActionForIteration()
    44  	change.processSet(func(value Change) {
    45  		element := value.ComputeDiffForBlock(block)
    46  		elements = append(elements, element)
    47  		current = collections.CompareActions(current, element.Action)
    48  	})
    49  	return elements, current
    50  }
    51  
    52  func (change Change) processSet(process func(value Change)) {
    53  	sliceValue := change.asSlice()
    54  
    55  	foundInBefore := make(map[int]int)
    56  	foundInAfter := make(map[int]int)
    57  
    58  	// O(n^2) operation here to find matching pairs in the set, so we can make
    59  	// the display look pretty. There might be a better way to do this, so look
    60  	// here for potential optimisations.
    61  
    62  	for ix := 0; ix < len(sliceValue.Before); ix++ {
    63  		matched := false
    64  		for jx := 0; jx < len(sliceValue.After); jx++ {
    65  			if _, ok := foundInAfter[jx]; ok {
    66  				// We've already found a match for this after value.
    67  				continue
    68  			}
    69  
    70  			child := sliceValue.getChild(ix, jx)
    71  			if reflect.DeepEqual(child.Before, child.After) && child.isBeforeSensitive() == child.isAfterSensitive() && !child.isUnknown() {
    72  				matched = true
    73  				foundInBefore[ix] = jx
    74  				foundInAfter[jx] = ix
    75  			}
    76  		}
    77  
    78  		if !matched {
    79  			foundInBefore[ix] = -1
    80  		}
    81  	}
    82  
    83  	clearRelevantStatus := func(change Change) Change {
    84  		// It's actually really difficult to render the diffs when some indices
    85  		// within a slice are relevant and others aren't. To make this simpler
    86  		// we just treat all children of a relevant list or set as also
    87  		// relevant.
    88  		//
    89  		// Interestingly the terraform plan builder also agrees with this, and
    90  		// never sets relevant attributes beneath lists or sets. We're just
    91  		// going to enforce this logic here as well. If the collection is
    92  		// relevant (decided elsewhere), then every element in the collection is
    93  		// also relevant. To be clear, in practice even if we didn't do the
    94  		// following explicitly the effect would be the same. It's just nicer
    95  		// for us to be clear about the behaviour we expect.
    96  		//
    97  		// What makes this difficult is the fact that the beforeIx and afterIx
    98  		// can be different, and it's quite difficult to work out which one is
    99  		// the relevant one. For nested lists, block lists, and tuples it's much
   100  		// easier because we always process the same indices in the before and
   101  		// after.
   102  		change.RelevantAttributes = attribute_path.AlwaysMatcher()
   103  		return change
   104  	}
   105  
   106  	// Now everything in before should be a key in foundInBefore and a value
   107  	// in foundInAfter. If a key is mapped to -1 in foundInBefore it means it
   108  	// does not have an equivalent in foundInAfter and so has been deleted.
   109  	// Everything in foundInAfter has a matching value in foundInBefore, but
   110  	// some values in after may not be in foundInAfter. This means these values
   111  	// are newly created.
   112  
   113  	for ix := 0; ix < len(sliceValue.Before); ix++ {
   114  		if jx := foundInBefore[ix]; jx >= 0 {
   115  			child := clearRelevantStatus(sliceValue.getChild(ix, jx))
   116  			process(child)
   117  			continue
   118  		}
   119  		child := clearRelevantStatus(sliceValue.getChild(ix, len(sliceValue.After)))
   120  		process(child)
   121  	}
   122  
   123  	for jx := 0; jx < len(sliceValue.After); jx++ {
   124  		if _, ok := foundInAfter[jx]; ok {
   125  			// Then this value was handled in the previous for loop.
   126  			continue
   127  		}
   128  		child := clearRelevantStatus(sliceValue.getChild(len(sliceValue.Before), jx))
   129  		process(child)
   130  	}
   131  }