github.com/opentofu/opentofu@v1.7.1/internal/command/jsonformat/differ/list.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 differ
     7  
     8  import (
     9  	"github.com/zclconf/go-cty/cty"
    10  
    11  	"github.com/opentofu/opentofu/internal/command/jsonformat/collections"
    12  	"github.com/opentofu/opentofu/internal/command/jsonformat/computed"
    13  	"github.com/opentofu/opentofu/internal/command/jsonformat/computed/renderers"
    14  	"github.com/opentofu/opentofu/internal/command/jsonformat/structured"
    15  	"github.com/opentofu/opentofu/internal/command/jsonformat/structured/attribute_path"
    16  	"github.com/opentofu/opentofu/internal/command/jsonprovider"
    17  	"github.com/opentofu/opentofu/internal/plans"
    18  )
    19  
    20  func computeAttributeDiffAsList(change structured.Change, elementType cty.Type) computed.Diff {
    21  	sliceValue := change.AsSlice()
    22  
    23  	processIndices := func(beforeIx, afterIx int) computed.Diff {
    24  		value := sliceValue.GetChild(beforeIx, afterIx)
    25  
    26  		// It's actually really difficult to render the diffs when some indices
    27  		// within a slice are relevant and others aren't. To make this simpler
    28  		// we just treat all children of a relevant list or set as also
    29  		// relevant.
    30  		//
    31  		// Interestingly the tofu plan builder also agrees with this, and
    32  		// never sets relevant attributes beneath lists or sets. We're just
    33  		// going to enforce this logic here as well. If the collection is
    34  		// relevant (decided elsewhere), then every element in the collection is
    35  		// also relevant. To be clear, in practice even if we didn't do the
    36  		// following explicitly the effect would be the same. It's just nicer
    37  		// for us to be clear about the behaviour we expect.
    38  		//
    39  		// What makes this difficult is the fact that the beforeIx and afterIx
    40  		// can be different, and it's quite difficult to work out which one is
    41  		// the relevant one. For nested lists, block lists, and tuples it's much
    42  		// easier because we always process the same indices in the before and
    43  		// after.
    44  		value.RelevantAttributes = attribute_path.AlwaysMatcher()
    45  
    46  		return ComputeDiffForType(value, elementType)
    47  	}
    48  
    49  	isObjType := func(_ interface{}) bool {
    50  		return elementType.IsObjectType()
    51  	}
    52  
    53  	elements, current := collections.TransformSlice(sliceValue.Before, sliceValue.After, processIndices, isObjType)
    54  	return computed.NewDiff(renderers.List(elements), current, change.ReplacePaths.Matches())
    55  }
    56  
    57  func computeAttributeDiffAsNestedList(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff {
    58  	var elements []computed.Diff
    59  	current := change.GetDefaultActionForIteration()
    60  	processNestedList(change, func(value structured.Change) {
    61  		element := computeDiffForNestedAttribute(value, &jsonprovider.NestedType{
    62  			Attributes:  attributes,
    63  			NestingMode: "single",
    64  		})
    65  		elements = append(elements, element)
    66  		current = collections.CompareActions(current, element.Action)
    67  	})
    68  	return computed.NewDiff(renderers.NestedList(elements), current, change.ReplacePaths.Matches())
    69  }
    70  
    71  func computeBlockDiffsAsList(change structured.Change, block *jsonprovider.Block) ([]computed.Diff, plans.Action) {
    72  	var elements []computed.Diff
    73  	current := change.GetDefaultActionForIteration()
    74  	processNestedList(change, func(value structured.Change) {
    75  		element := ComputeDiffForBlock(value, block)
    76  		elements = append(elements, element)
    77  		current = collections.CompareActions(current, element.Action)
    78  	})
    79  	return elements, current
    80  }
    81  
    82  func processNestedList(change structured.Change, process func(value structured.Change)) {
    83  	sliceValue := change.AsSlice()
    84  	for ix := 0; ix < len(sliceValue.Before) || ix < len(sliceValue.After); ix++ {
    85  		value := sliceValue.GetChild(ix, ix)
    86  		if !value.RelevantAttributes.MatchesPartial() {
    87  			// Mark non-relevant attributes as unchanged.
    88  			value = value.AsNoOp()
    89  		}
    90  		process(value)
    91  	}
    92  }