mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-21 10:47:34 -05:00
113 lines
3.4 KiB
Go
113 lines
3.4 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package renderers
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/opentofu/opentofu/internal/command/jsonformat/computed"
|
|
|
|
"github.com/opentofu/opentofu/internal/plans"
|
|
)
|
|
|
|
var _ computed.DiffRenderer = (*mapRenderer)(nil)
|
|
|
|
func Map(elements map[string]computed.Diff) computed.DiffRenderer {
|
|
return &mapRenderer{
|
|
elements: elements,
|
|
alignKeys: true,
|
|
}
|
|
}
|
|
|
|
func NestedMap(elements map[string]computed.Diff) computed.DiffRenderer {
|
|
return &mapRenderer{
|
|
elements: elements,
|
|
overrideNullSuffix: true,
|
|
overrideForcesReplacement: true,
|
|
}
|
|
}
|
|
|
|
type mapRenderer struct {
|
|
NoWarningsRenderer
|
|
|
|
elements map[string]computed.Diff
|
|
|
|
overrideNullSuffix bool
|
|
overrideForcesReplacement bool
|
|
alignKeys bool
|
|
}
|
|
|
|
func (renderer mapRenderer) RenderHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {
|
|
forcesReplacementSelf := diff.Replace && !renderer.overrideForcesReplacement
|
|
forcesReplacementChildren := diff.Replace && renderer.overrideForcesReplacement
|
|
|
|
if len(renderer.elements) == 0 {
|
|
return fmt.Sprintf("{}%s%s", nullSuffix(diff.Action, opts), forcesReplacement(forcesReplacementSelf, opts))
|
|
}
|
|
|
|
// Sort the map elements by key, so we have a deterministic ordering in
|
|
// the output.
|
|
var keys []string
|
|
|
|
// We need to make sure the keys are capable of rendering properly.
|
|
escapedKeys := make(map[string]string)
|
|
|
|
maximumKeyLen := 0
|
|
for key := range renderer.elements {
|
|
keys = append(keys, key)
|
|
|
|
escapedKey := hclEscapeString(key)
|
|
escapedKeys[key] = escapedKey
|
|
if maximumKeyLen < len(escapedKey) {
|
|
maximumKeyLen = len(escapedKey)
|
|
}
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
unchangedElements := 0
|
|
|
|
elementOpts := opts.Clone()
|
|
elementOpts.OverrideNullSuffix = diff.Action == plans.Delete || renderer.overrideNullSuffix
|
|
elementOpts.OverrideForcesReplacement = forcesReplacementChildren
|
|
|
|
var buf bytes.Buffer
|
|
buf.WriteString(fmt.Sprintf("{%s\n", forcesReplacement(forcesReplacementSelf, opts)))
|
|
for _, key := range keys {
|
|
element := renderer.elements[key]
|
|
|
|
if element.Action == plans.NoOp && !opts.ShowUnchangedChildren {
|
|
// Don't render NoOp operations when we are compact display.
|
|
unchangedElements++
|
|
continue
|
|
}
|
|
|
|
for _, warning := range element.WarningsHuman(indent+1, opts) {
|
|
buf.WriteString(fmt.Sprintf("%s%s\n", formatIndent(indent+1), warning))
|
|
}
|
|
// Only show commas between elements for objects.
|
|
comma := ""
|
|
if _, ok := element.Renderer.(*objectRenderer); ok {
|
|
comma = ","
|
|
}
|
|
|
|
if renderer.alignKeys {
|
|
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))
|
|
} else {
|
|
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))
|
|
}
|
|
|
|
}
|
|
|
|
if unchangedElements > 0 {
|
|
buf.WriteString(fmt.Sprintf("%s%s%s\n", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts), unchanged("element", unchangedElements, opts)))
|
|
}
|
|
|
|
buf.WriteString(fmt.Sprintf("%s%s}%s", formatIndent(indent), writeDiffActionSymbol(plans.NoOp, opts), nullSuffix(diff.Action, opts)))
|
|
return buf.String()
|
|
}
|