github.com/opentofu/opentofu@v1.7.1/internal/command/jsonformat/computed/renderers/map.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 renderers
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"sort"
    12  
    13  	"github.com/opentofu/opentofu/internal/command/jsonformat/computed"
    14  
    15  	"github.com/opentofu/opentofu/internal/plans"
    16  )
    17  
    18  var _ computed.DiffRenderer = (*mapRenderer)(nil)
    19  
    20  func Map(elements map[string]computed.Diff) computed.DiffRenderer {
    21  	return &mapRenderer{
    22  		elements:  elements,
    23  		alignKeys: true,
    24  	}
    25  }
    26  
    27  func NestedMap(elements map[string]computed.Diff) computed.DiffRenderer {
    28  	return &mapRenderer{
    29  		elements:                  elements,
    30  		overrideNullSuffix:        true,
    31  		overrideForcesReplacement: true,
    32  	}
    33  }
    34  
    35  type mapRenderer struct {
    36  	NoWarningsRenderer
    37  
    38  	elements map[string]computed.Diff
    39  
    40  	overrideNullSuffix        bool
    41  	overrideForcesReplacement bool
    42  	alignKeys                 bool
    43  }
    44  
    45  func (renderer mapRenderer) RenderHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {
    46  	forcesReplacementSelf := diff.Replace && !renderer.overrideForcesReplacement
    47  	forcesReplacementChildren := diff.Replace && renderer.overrideForcesReplacement
    48  
    49  	if len(renderer.elements) == 0 {
    50  		return fmt.Sprintf("{}%s%s", nullSuffix(diff.Action, opts), forcesReplacement(forcesReplacementSelf, opts))
    51  	}
    52  
    53  	// Sort the map elements by key, so we have a deterministic ordering in
    54  	// the output.
    55  	var keys []string
    56  
    57  	// We need to make sure the keys are capable of rendering properly.
    58  	escapedKeys := make(map[string]string)
    59  
    60  	maximumKeyLen := 0
    61  	for key := range renderer.elements {
    62  		keys = append(keys, key)
    63  
    64  		escapedKey := hclEscapeString(key)
    65  		escapedKeys[key] = escapedKey
    66  		if maximumKeyLen < len(escapedKey) {
    67  			maximumKeyLen = len(escapedKey)
    68  		}
    69  	}
    70  	sort.Strings(keys)
    71  
    72  	unchangedElements := 0
    73  
    74  	elementOpts := opts.Clone()
    75  	elementOpts.OverrideNullSuffix = diff.Action == plans.Delete || renderer.overrideNullSuffix
    76  	elementOpts.OverrideForcesReplacement = forcesReplacementChildren
    77  
    78  	var buf bytes.Buffer
    79  	buf.WriteString(fmt.Sprintf("{%s\n", forcesReplacement(forcesReplacementSelf, opts)))
    80  	for _, key := range keys {
    81  		element := renderer.elements[key]
    82  
    83  		if element.Action == plans.NoOp && !opts.ShowUnchangedChildren {
    84  			// Don't render NoOp operations when we are compact display.
    85  			unchangedElements++
    86  			continue
    87  		}
    88  
    89  		for _, warning := range element.WarningsHuman(indent+1, opts) {
    90  			buf.WriteString(fmt.Sprintf("%s%s\n", formatIndent(indent+1), warning))
    91  		}
    92  		// Only show commas between elements for objects.
    93  		comma := ""
    94  		if _, ok := element.Renderer.(*objectRenderer); ok {
    95  			comma = ","
    96  		}
    97  
    98  		if renderer.alignKeys {
    99  			buf.WriteString(fmt.Sprintf("%s%s%-*s = %s%s\n", formatIndent(indent+1), writeDiffActionSymbol(element.Action, elementOpts), maximumKeyLen, escapedKeys[key], element.RenderHuman(indent+1, elementOpts), comma))
   100  		} else {
   101  			buf.WriteString(fmt.Sprintf("%s%s%s = %s%s\n", formatIndent(indent+1), writeDiffActionSymbol(element.Action, elementOpts), escapedKeys[key], element.RenderHuman(indent+1, elementOpts), comma))
   102  		}
   103  
   104  	}
   105  
   106  	if unchangedElements > 0 {
   107  		buf.WriteString(fmt.Sprintf("%s%s%s\n", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts), unchanged("element", unchangedElements, opts)))
   108  	}
   109  
   110  	buf.WriteString(fmt.Sprintf("%s%s}%s", formatIndent(indent), writeDiffActionSymbol(plans.NoOp, opts), nullSuffix(diff.Action, opts)))
   111  	return buf.String()
   112  }