github.com/opentofu/opentofu@v1.7.1/internal/command/jsonformat/computed/renderers/object.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  	"github.com/opentofu/opentofu/internal/plans"
    15  )
    16  
    17  var _ computed.DiffRenderer = (*objectRenderer)(nil)
    18  
    19  func Object(attributes map[string]computed.Diff) computed.DiffRenderer {
    20  	return &objectRenderer{
    21  		attributes:         attributes,
    22  		overrideNullSuffix: true,
    23  	}
    24  }
    25  
    26  func NestedObject(attributes map[string]computed.Diff) computed.DiffRenderer {
    27  	return &objectRenderer{
    28  		attributes:         attributes,
    29  		overrideNullSuffix: false,
    30  	}
    31  }
    32  
    33  type objectRenderer struct {
    34  	NoWarningsRenderer
    35  
    36  	attributes         map[string]computed.Diff
    37  	overrideNullSuffix bool
    38  }
    39  
    40  func (renderer objectRenderer) RenderHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {
    41  	if len(renderer.attributes) == 0 {
    42  		return fmt.Sprintf("{}%s%s", nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts))
    43  	}
    44  
    45  	attributeOpts := opts.Clone()
    46  	attributeOpts.OverrideNullSuffix = renderer.overrideNullSuffix
    47  
    48  	// We need to keep track of our keys in two ways. The first is the order in
    49  	// which we will display them. The second is a mapping to their safely
    50  	// escaped equivalent.
    51  
    52  	maximumKeyLen := 0
    53  	var keys []string
    54  	escapedKeys := make(map[string]string)
    55  	for key := range renderer.attributes {
    56  		keys = append(keys, key)
    57  		escapedKey := EnsureValidAttributeName(key)
    58  		escapedKeys[key] = escapedKey
    59  		if maximumKeyLen < len(escapedKey) {
    60  			maximumKeyLen = len(escapedKey)
    61  		}
    62  	}
    63  	sort.Strings(keys)
    64  
    65  	unchangedAttributes := 0
    66  	var buf bytes.Buffer
    67  	buf.WriteString(fmt.Sprintf("{%s\n", forcesReplacement(diff.Replace, opts)))
    68  	for _, key := range keys {
    69  		attribute := renderer.attributes[key]
    70  
    71  		if importantAttribute(key) {
    72  			importantAttributeOpts := attributeOpts.Clone()
    73  			importantAttributeOpts.ShowUnchangedChildren = true
    74  
    75  			for _, warning := range attribute.WarningsHuman(indent+1, importantAttributeOpts) {
    76  				buf.WriteString(fmt.Sprintf("%s%s\n", formatIndent(indent+1), warning))
    77  			}
    78  			buf.WriteString(fmt.Sprintf("%s%s%-*s = %s\n", formatIndent(indent+1), writeDiffActionSymbol(attribute.Action, importantAttributeOpts), maximumKeyLen, escapedKeys[key], attribute.RenderHuman(indent+1, importantAttributeOpts)))
    79  			continue
    80  		}
    81  
    82  		if attribute.Action == plans.NoOp && !opts.ShowUnchangedChildren {
    83  			// Don't render NoOp operations when we are compact display.
    84  			unchangedAttributes++
    85  			continue
    86  		}
    87  
    88  		for _, warning := range attribute.WarningsHuman(indent+1, opts) {
    89  			buf.WriteString(fmt.Sprintf("%s%s\n", formatIndent(indent+1), warning))
    90  		}
    91  		buf.WriteString(fmt.Sprintf("%s%s%-*s = %s\n", formatIndent(indent+1), writeDiffActionSymbol(attribute.Action, attributeOpts), maximumKeyLen, escapedKeys[key], attribute.RenderHuman(indent+1, attributeOpts)))
    92  	}
    93  
    94  	if unchangedAttributes > 0 {
    95  		buf.WriteString(fmt.Sprintf("%s%s%s\n", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts), unchanged("attribute", unchangedAttributes, opts)))
    96  	}
    97  
    98  	buf.WriteString(fmt.Sprintf("%s%s}%s", formatIndent(indent), writeDiffActionSymbol(plans.NoOp, opts), nullSuffix(diff.Action, opts)))
    99  	return buf.String()
   100  }