mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-21 02:37:43 -05:00
130 lines
5.0 KiB
Go
130 lines
5.0 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"
|
|
|
|
"github.com/opentofu/opentofu/internal/command/jsonformat/computed"
|
|
"github.com/opentofu/opentofu/internal/plans"
|
|
)
|
|
|
|
var _ computed.DiffRenderer = (*listRenderer)(nil)
|
|
|
|
func List(elements []computed.Diff) computed.DiffRenderer {
|
|
return &listRenderer{
|
|
displayContext: true,
|
|
elements: elements,
|
|
}
|
|
}
|
|
|
|
func NestedList(elements []computed.Diff) computed.DiffRenderer {
|
|
return &listRenderer{
|
|
elements: elements,
|
|
}
|
|
}
|
|
|
|
type listRenderer struct {
|
|
NoWarningsRenderer
|
|
|
|
displayContext bool
|
|
elements []computed.Diff
|
|
}
|
|
|
|
func (renderer listRenderer) RenderHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {
|
|
if len(renderer.elements) == 0 {
|
|
return fmt.Sprintf("[]%s%s", nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts))
|
|
}
|
|
|
|
elementOpts := opts.Clone()
|
|
elementOpts.OverrideNullSuffix = true
|
|
|
|
unchangedElementOpts := opts.Clone()
|
|
unchangedElementOpts.ShowUnchangedChildren = true
|
|
|
|
var unchangedElements []computed.Diff
|
|
|
|
// renderNext tells the renderer to print out the next element in the list
|
|
// whatever state it is in. So, even if a change is a NoOp we will still
|
|
// print it out if the last change we processed wants us to.
|
|
renderNext := false
|
|
|
|
var buf bytes.Buffer
|
|
buf.WriteString(fmt.Sprintf("[%s\n", forcesReplacement(diff.Replace, opts)))
|
|
for _, element := range renderer.elements {
|
|
if element.Action == plans.NoOp && !renderNext && !opts.ShowUnchangedChildren {
|
|
unchangedElements = append(unchangedElements, element)
|
|
continue
|
|
}
|
|
renderNext = false
|
|
|
|
opts := elementOpts
|
|
|
|
// If we want to display the context around this change, we want to
|
|
// render the change immediately before this change in the list, and the
|
|
// change immediately after in the list, even if both these changes are
|
|
// NoOps. This will give the user reading the diff some context as to
|
|
// where in the list these changes are being made, as order matters.
|
|
if renderer.displayContext {
|
|
// If our list of unchanged elements contains more than one entry
|
|
// we'll print out a count of the number of unchanged elements that
|
|
// we skipped. Note, this is the length of the unchanged elements
|
|
// minus 1 as the most recent unchanged element will be printed out
|
|
// in full.
|
|
if len(unchangedElements) > 1 {
|
|
buf.WriteString(fmt.Sprintf("%s%s%s\n", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts), unchanged("element", len(unchangedElements)-1, opts)))
|
|
}
|
|
// If our list of unchanged elements contains at least one entry,
|
|
// we're going to print out the most recent change in full. That's
|
|
// what happens here.
|
|
if len(unchangedElements) > 0 {
|
|
lastElement := unchangedElements[len(unchangedElements)-1]
|
|
buf.WriteString(fmt.Sprintf("%s%s%s,\n", formatIndent(indent+1), writeDiffActionSymbol(lastElement.Action, unchangedElementOpts), lastElement.RenderHuman(indent+1, unchangedElementOpts)))
|
|
}
|
|
// We now reset the unchanged elements list, we've printed out a
|
|
// count of all the elements we skipped so we start counting from
|
|
// scratch again. This means that if we process a run of changed
|
|
// elements, they won't all start printing out summaries of every
|
|
// change that happened previously.
|
|
unchangedElements = nil
|
|
|
|
if element.Action == plans.NoOp {
|
|
// If this is a NoOp action then we're going to render it below
|
|
// so we need to just override the opts we're going to use to
|
|
// make sure we use the unchanged opts.
|
|
opts = unchangedElementOpts
|
|
} else {
|
|
// As we also want to render the element immediately after any
|
|
// changes, we make a note here to say we should render the next
|
|
// change whatever it is. But, we only want to render the next
|
|
// change if the current change isn't a NoOp. If the current change
|
|
// is a NoOp then it was told to print by the last change and we
|
|
// don't want to cascade and print all changes from now on.
|
|
renderNext = true
|
|
}
|
|
}
|
|
|
|
for _, warning := range element.WarningsHuman(indent+1, opts) {
|
|
buf.WriteString(fmt.Sprintf("%s%s\n", formatIndent(indent+1), warning))
|
|
}
|
|
buf.WriteString(fmt.Sprintf("%s%s%s,\n", formatIndent(indent+1), writeDiffActionSymbol(element.Action, opts), element.RenderHuman(indent+1, opts)))
|
|
}
|
|
|
|
// If we were not displaying any context alongside our changes then the
|
|
// unchangedElements list will contain every unchanged element, and we'll
|
|
// print that out as we do with every other collection.
|
|
//
|
|
// If we were displaying context, then this will contain any unchanged
|
|
// elements since our last change, so we should also print it out.
|
|
if len(unchangedElements) > 0 {
|
|
buf.WriteString(fmt.Sprintf("%s%s%s\n", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts), unchanged("element", len(unchangedElements), opts)))
|
|
}
|
|
|
|
buf.WriteString(fmt.Sprintf("%s%s]%s", formatIndent(indent), writeDiffActionSymbol(plans.NoOp, opts), nullSuffix(diff.Action, opts)))
|
|
return buf.String()
|
|
}
|