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 }