| package renderers |
| |
| import ( |
| "bytes" |
| "fmt" |
| "sort" |
| |
| "github.com/hashicorp/terraform/internal/command/jsonformat/computed" |
| |
| "github.com/hashicorp/terraform/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() |
| } |