github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/jsonformat/computed/diff.go (about)

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