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

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package differ
     5  
     6  import (
     7  	"github.com/terramate-io/tf/command/jsonformat/collections"
     8  	"github.com/terramate-io/tf/command/jsonformat/computed"
     9  	"github.com/terramate-io/tf/command/jsonformat/computed/renderers"
    10  	"github.com/terramate-io/tf/command/jsonformat/structured"
    11  	"github.com/terramate-io/tf/command/jsonprovider"
    12  	"github.com/terramate-io/tf/plans"
    13  )
    14  
    15  func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) computed.Diff {
    16  	if sensitive, ok := checkForSensitiveBlock(change, block); ok {
    17  		return sensitive
    18  	}
    19  
    20  	if unknown, ok := checkForUnknownBlock(change, block); ok {
    21  		return unknown
    22  	}
    23  
    24  	current := change.GetDefaultActionForIteration()
    25  
    26  	blockValue := change.AsMap()
    27  
    28  	attributes := make(map[string]computed.Diff)
    29  	for key, attr := range block.Attributes {
    30  		childValue := blockValue.GetChild(key)
    31  
    32  		if !childValue.RelevantAttributes.MatchesPartial() {
    33  			// Mark non-relevant attributes as unchanged.
    34  			childValue = childValue.AsNoOp()
    35  		}
    36  
    37  		// Empty strings in blocks should be considered null for legacy reasons.
    38  		// The SDK doesn't support null strings yet, so we work around this now.
    39  		if before, ok := childValue.Before.(string); ok && len(before) == 0 {
    40  			childValue.Before = nil
    41  		}
    42  		if after, ok := childValue.After.(string); ok && len(after) == 0 {
    43  			childValue.After = nil
    44  		}
    45  
    46  		// Always treat changes to blocks as implicit.
    47  		childValue.BeforeExplicit = false
    48  		childValue.AfterExplicit = false
    49  
    50  		childChange := ComputeDiffForAttribute(childValue, attr)
    51  		if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
    52  			// Don't record nil values at all in blocks.
    53  			continue
    54  		}
    55  
    56  		attributes[key] = childChange
    57  		current = collections.CompareActions(current, childChange.Action)
    58  	}
    59  
    60  	blocks := renderers.Blocks{
    61  		ReplaceBlocks:         make(map[string]bool),
    62  		BeforeSensitiveBlocks: make(map[string]bool),
    63  		AfterSensitiveBlocks:  make(map[string]bool),
    64  		SingleBlocks:          make(map[string]computed.Diff),
    65  		ListBlocks:            make(map[string][]computed.Diff),
    66  		SetBlocks:             make(map[string][]computed.Diff),
    67  		MapBlocks:             make(map[string]map[string]computed.Diff),
    68  	}
    69  
    70  	for key, blockType := range block.BlockTypes {
    71  		childValue := blockValue.GetChild(key)
    72  
    73  		if !childValue.RelevantAttributes.MatchesPartial() {
    74  			// Mark non-relevant attributes as unchanged.
    75  			childValue = childValue.AsNoOp()
    76  		}
    77  
    78  		beforeSensitive := childValue.IsBeforeSensitive()
    79  		afterSensitive := childValue.IsAfterSensitive()
    80  		forcesReplacement := childValue.ReplacePaths.Matches()
    81  
    82  		switch NestingMode(blockType.NestingMode) {
    83  		case nestingModeSet:
    84  			diffs, action := computeBlockDiffsAsSet(childValue, blockType.Block)
    85  			if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
    86  				// Don't record nil values in blocks.
    87  				continue
    88  			}
    89  			blocks.AddAllSetBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
    90  			current = collections.CompareActions(current, action)
    91  		case nestingModeList:
    92  			diffs, action := computeBlockDiffsAsList(childValue, blockType.Block)
    93  			if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
    94  				// Don't record nil values in blocks.
    95  				continue
    96  			}
    97  			blocks.AddAllListBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
    98  			current = collections.CompareActions(current, action)
    99  		case nestingModeMap:
   100  			diffs, action := computeBlockDiffsAsMap(childValue, blockType.Block)
   101  			if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
   102  				// Don't record nil values in blocks.
   103  				continue
   104  			}
   105  			blocks.AddAllMapBlocks(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
   106  			current = collections.CompareActions(current, action)
   107  		case nestingModeSingle, nestingModeGroup:
   108  			diff := ComputeDiffForBlock(childValue, blockType.Block)
   109  			if diff.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
   110  				// Don't record nil values in blocks.
   111  				continue
   112  			}
   113  			blocks.AddSingleBlock(key, diff, forcesReplacement, beforeSensitive, afterSensitive)
   114  			current = collections.CompareActions(current, diff.Action)
   115  		default:
   116  			panic("unrecognized nesting mode: " + blockType.NestingMode)
   117  		}
   118  	}
   119  
   120  	return computed.NewDiff(renderers.Block(attributes, blocks), current, change.ReplacePaths.Matches())
   121  }