github.com/opentofu/opentofu@v1.7.1/internal/command/jsonformat/computed/diff.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 computed
     7  
     8  import (
     9  	"github.com/mitchellh/colorstring"
    10  
    11  	"github.com/opentofu/opentofu/internal/plans"
    12  )
    13  
    14  // Diff captures the computed diff for a single block, element or attribute.
    15  //
    16  // It essentially merges common functionality across all types of changes,
    17  // namely the replace logic and the action / change type. Any remaining
    18  // behaviour can be offloaded to the renderer which will be unique for the
    19  // various change types (eg. maps, objects, lists, blocks, primitives, etc.).
    20  type Diff struct {
    21  	// Renderer captures the uncommon functionality across the different kinds
    22  	// of changes. Each type of change (lists, blocks, sets, etc.) will have a
    23  	// unique renderer.
    24  	Renderer DiffRenderer
    25  
    26  	// Action is the action described by this change (such as create, delete,
    27  	// update, etc.).
    28  	Action plans.Action
    29  
    30  	// Replace tells the Change that it should add the `# forces replacement`
    31  	// suffix.
    32  	//
    33  	// Every single change could potentially add this suffix, so we embed it in
    34  	// the change as common functionality instead of in the specific renderers.
    35  	Replace bool
    36  }
    37  
    38  // NewDiff creates a new Diff object with the provided renderer, action and
    39  // replace context.
    40  func NewDiff(renderer DiffRenderer, action plans.Action, replace bool) Diff {
    41  	return Diff{
    42  		Renderer: renderer,
    43  		Action:   action,
    44  		Replace:  replace,
    45  	}
    46  }
    47  
    48  // RenderHuman prints the Change into a human-readable string referencing the
    49  // specified RenderOpts.
    50  //
    51  // If the returned string is a single line, then indent should be ignored.
    52  //
    53  // If the return string is multiple lines, then indent should be used to offset
    54  // the beginning of all lines but the first by the specified amount.
    55  func (diff Diff) RenderHuman(indent int, opts RenderHumanOpts) string {
    56  	return diff.Renderer.RenderHuman(diff, indent, opts)
    57  }
    58  
    59  // WarningsHuman returns a list of strings that should be rendered as warnings
    60  // before a given change is rendered.
    61  //
    62  // As with the RenderHuman function, the indent should only be applied on
    63  // multiline warnings and on the second and following lines.
    64  func (diff Diff) WarningsHuman(indent int, opts RenderHumanOpts) []string {
    65  	return diff.Renderer.WarningsHuman(diff, indent, opts)
    66  }
    67  
    68  type DiffRenderer interface {
    69  	RenderHuman(diff Diff, indent int, opts RenderHumanOpts) string
    70  	WarningsHuman(diff Diff, indent int, opts RenderHumanOpts) []string
    71  }
    72  
    73  // RenderHumanOpts contains options that can control how the human render
    74  // function of the DiffRenderer will function.
    75  type RenderHumanOpts struct {
    76  	Colorize *colorstring.Colorize
    77  
    78  	// OverrideNullSuffix tells the Renderer not to display the `-> null` suffix
    79  	// that is normally displayed when an element, attribute, or block is
    80  	// deleted.
    81  	OverrideNullSuffix bool
    82  
    83  	// OverrideForcesReplacement tells the Renderer to display the
    84  	// `# forces replacement` suffix, even if a diff doesn't have the Replace
    85  	// field set.
    86  	//
    87  	// Some renderers (like the Set renderer) don't display the suffix
    88  	// themselves but force their child diffs to display it instead.
    89  	OverrideForcesReplacement bool
    90  
    91  	// ShowUnchangedChildren instructs the Renderer to render all children of a
    92  	// given complex change, instead of hiding unchanged items and compressing
    93  	// them into a single line.
    94  	ShowUnchangedChildren bool
    95  
    96  	// HideDiffActionSymbols tells the renderer not to show the '+'/'-' symbols
    97  	// and to skip the places where the symbols would result in an offset.
    98  	HideDiffActionSymbols bool
    99  }
   100  
   101  // NewRenderHumanOpts creates a new RenderHumanOpts struct with the required
   102  // fields set.
   103  func NewRenderHumanOpts(colorize *colorstring.Colorize) RenderHumanOpts {
   104  	return RenderHumanOpts{
   105  		Colorize: colorize,
   106  	}
   107  }
   108  
   109  // Clone returns a new RenderOpts object, that matches the original but can be
   110  // edited without changing the original.
   111  func (opts RenderHumanOpts) Clone() RenderHumanOpts {
   112  	return RenderHumanOpts{
   113  		Colorize: opts.Colorize,
   114  
   115  		OverrideNullSuffix:    opts.OverrideNullSuffix,
   116  		ShowUnchangedChildren: opts.ShowUnchangedChildren,
   117  		HideDiffActionSymbols: opts.HideDiffActionSymbols,
   118  
   119  		// OverrideForcesReplacement is a special case in that it doesn't
   120  		// cascade. So each diff should decide independently whether it's direct
   121  		// children should override their internal Replace logic, instead of
   122  		// an ancestor making the switch and affecting the entire tree.
   123  		OverrideForcesReplacement: false,
   124  	}
   125  }