// Copyright (c) The OpenTofu Authors // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2023 HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package computed import ( "github.com/mitchellh/colorstring" "github.com/opentofu/opentofu/internal/plans" ) // Diff captures the computed diff for a single block, element or attribute. // // It essentially merges common functionality across all types of changes, // namely the replace logic and the action / change type. Any remaining // behaviour can be offloaded to the renderer which will be unique for the // various change types (eg. maps, objects, lists, blocks, primitives, etc.). type Diff struct { // Renderer captures the uncommon functionality across the different kinds // of changes. Each type of change (lists, blocks, sets, etc.) will have a // unique renderer. Renderer DiffRenderer // Action is the action described by this change (such as create, delete, // update, etc.). Action plans.Action // Replace tells the Change that it should add the `# forces replacement` // suffix. // // Every single change could potentially add this suffix, so we embed it in // the change as common functionality instead of in the specific renderers. Replace bool } // NewDiff creates a new Diff object with the provided renderer, action and // replace context. func NewDiff(renderer DiffRenderer, action plans.Action, replace bool) Diff { return Diff{ Renderer: renderer, Action: action, Replace: replace, } } // RenderHuman prints the Change into a human-readable string referencing the // specified RenderOpts. // // If the returned string is a single line, then indent should be ignored. // // If the return string is multiple lines, then indent should be used to offset // the beginning of all lines but the first by the specified amount. func (diff Diff) RenderHuman(indent int, opts RenderHumanOpts) string { return diff.Renderer.RenderHuman(diff, indent, opts) } // WarningsHuman returns a list of strings that should be rendered as warnings // before a given change is rendered. // // As with the RenderHuman function, the indent should only be applied on // multiline warnings and on the second and following lines. func (diff Diff) WarningsHuman(indent int, opts RenderHumanOpts) []string { return diff.Renderer.WarningsHuman(diff, indent, opts) } type DiffRenderer interface { RenderHuman(diff Diff, indent int, opts RenderHumanOpts) string WarningsHuman(diff Diff, indent int, opts RenderHumanOpts) []string } // RenderHumanOpts contains options that can control how the human render // function of the DiffRenderer will function. type RenderHumanOpts struct { Colorize *colorstring.Colorize // OverrideNullSuffix tells the Renderer not to display the `-> null` suffix // that is normally displayed when an element, attribute, or block is // deleted. OverrideNullSuffix bool // OverrideForcesReplacement tells the Renderer to display the // `# forces replacement` suffix, even if a diff doesn't have the Replace // field set. // // Some renderers (like the Set renderer) don't display the suffix // themselves but force their child diffs to display it instead. OverrideForcesReplacement bool // ShowUnchangedChildren instructs the Renderer to render all children of a // given complex change, instead of hiding unchanged items and compressing // them into a single line. ShowUnchangedChildren bool // HideDiffActionSymbols tells the renderer not to show the '+'/'-' symbols // and to skip the places where the symbols would result in an offset. HideDiffActionSymbols bool // ShowSensitive is used to display the value of variables marked as sensitive. ShowSensitive bool } // NewRenderHumanOpts creates a new RenderHumanOpts struct with the required // fields set. func NewRenderHumanOpts(colorize *colorstring.Colorize, showSensitive bool) RenderHumanOpts { return RenderHumanOpts{ Colorize: colorize, ShowSensitive: showSensitive, } } // Clone returns a new RenderOpts object, that matches the original but can be // edited without changing the original. func (opts RenderHumanOpts) Clone() RenderHumanOpts { return RenderHumanOpts{ Colorize: opts.Colorize, OverrideNullSuffix: opts.OverrideNullSuffix, ShowUnchangedChildren: opts.ShowUnchangedChildren, HideDiffActionSymbols: opts.HideDiffActionSymbols, // OverrideForcesReplacement is a special case in that it doesn't // cascade. So each diff should decide independently whether it's direct // children should override their internal Replace logic, instead of // an ancestor making the switch and affecting the entire tree. OverrideForcesReplacement: false, ShowSensitive: opts.ShowSensitive, } }