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 }