github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonformat/computed/renderers/primitive.go (about)

     1  package renderers
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"strings"
     7  
     8  	"github.com/zclconf/go-cty/cty"
     9  
    10  	"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
    11  	"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
    12  	"github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path"
    13  	"github.com/hashicorp/terraform/internal/plans"
    14  )
    15  
    16  var _ computed.DiffRenderer = (*primitiveRenderer)(nil)
    17  
    18  func Primitive(before, after interface{}, ctype cty.Type) computed.DiffRenderer {
    19  	return &primitiveRenderer{
    20  		before: before,
    21  		after:  after,
    22  		ctype:  ctype,
    23  	}
    24  }
    25  
    26  type primitiveRenderer struct {
    27  	NoWarningsRenderer
    28  
    29  	before interface{}
    30  	after  interface{}
    31  	ctype  cty.Type
    32  }
    33  
    34  func (renderer primitiveRenderer) RenderHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {
    35  	if renderer.ctype == cty.String {
    36  		return renderer.renderStringDiff(diff, indent, opts)
    37  	}
    38  
    39  	beforeValue := renderPrimitiveValue(renderer.before, renderer.ctype, opts)
    40  	afterValue := renderPrimitiveValue(renderer.after, renderer.ctype, opts)
    41  
    42  	switch diff.Action {
    43  	case plans.Create:
    44  		return fmt.Sprintf("%s%s", afterValue, forcesReplacement(diff.Replace, opts))
    45  	case plans.Delete:
    46  		return fmt.Sprintf("%s%s%s", beforeValue, nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts))
    47  	case plans.NoOp:
    48  		return fmt.Sprintf("%s%s", beforeValue, forcesReplacement(diff.Replace, opts))
    49  	default:
    50  		return fmt.Sprintf("%s %s %s%s", beforeValue, opts.Colorize.Color("[yellow]->[reset]"), afterValue, forcesReplacement(diff.Replace, opts))
    51  	}
    52  }
    53  
    54  func renderPrimitiveValue(value interface{}, t cty.Type, opts computed.RenderHumanOpts) string {
    55  	if value == nil {
    56  		return opts.Colorize.Color("[dark_gray]null[reset]")
    57  	}
    58  
    59  	switch {
    60  	case t == cty.Bool:
    61  		if value.(bool) {
    62  			return "true"
    63  		}
    64  		return "false"
    65  	case t == cty.Number:
    66  		bf := big.NewFloat(value.(float64))
    67  		return bf.Text('f', -1)
    68  	default:
    69  		panic("unrecognized primitive type: " + t.FriendlyName())
    70  	}
    71  }
    72  
    73  func (renderer primitiveRenderer) renderStringDiff(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {
    74  
    75  	// We process multiline strings at the end of the switch statement.
    76  	var lines []string
    77  
    78  	switch diff.Action {
    79  	case plans.Create, plans.NoOp:
    80  		str := evaluatePrimitiveString(renderer.after, opts)
    81  
    82  		if str.Json != nil {
    83  			if diff.Action == plans.NoOp {
    84  				return renderer.renderStringDiffAsJson(diff, indent, opts, str, str)
    85  			} else {
    86  				return renderer.renderStringDiffAsJson(diff, indent, opts, evaluatedString{}, str)
    87  			}
    88  		}
    89  
    90  		if !str.IsMultiline {
    91  			return fmt.Sprintf("%q%s", str.String, forcesReplacement(diff.Replace, opts))
    92  		}
    93  
    94  		// We are creating a single multiline string, so let's split by the new
    95  		// line character. While we are doing this, we are going to insert our
    96  		// indents and make sure each line is formatted correctly.
    97  		lines = strings.Split(strings.ReplaceAll(str.String, "\n", fmt.Sprintf("\n%s%s", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts))), "\n")
    98  
    99  		// We now just need to do the same for the first entry in lines, because
   100  		// we split on the new line characters which won't have been at the
   101  		// beginning of the first line.
   102  		lines[0] = fmt.Sprintf("%s%s%s", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts), lines[0])
   103  	case plans.Delete:
   104  		str := evaluatePrimitiveString(renderer.before, opts)
   105  
   106  		if str.Json != nil {
   107  			return renderer.renderStringDiffAsJson(diff, indent, opts, str, evaluatedString{})
   108  		}
   109  
   110  		if !str.IsMultiline {
   111  			return fmt.Sprintf("%q%s%s", str.String, nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts))
   112  		}
   113  
   114  		// We are creating a single multiline string, so let's split by the new
   115  		// line character. While we are doing this, we are going to insert our
   116  		// indents and make sure each line is formatted correctly.
   117  		lines = strings.Split(strings.ReplaceAll(str.String, "\n", fmt.Sprintf("\n%s%s", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts))), "\n")
   118  
   119  		// We now just need to do the same for the first entry in lines, because
   120  		// we split on the new line characters which won't have been at the
   121  		// beginning of the first line.
   122  		lines[0] = fmt.Sprintf("%s%s%s", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts), lines[0])
   123  	default:
   124  		beforeString := evaluatePrimitiveString(renderer.before, opts)
   125  		afterString := evaluatePrimitiveString(renderer.after, opts)
   126  
   127  		if beforeString.Json != nil && afterString.Json != nil {
   128  			return renderer.renderStringDiffAsJson(diff, indent, opts, beforeString, afterString)
   129  		}
   130  
   131  		if beforeString.Json != nil || afterString.Json != nil {
   132  			// This means one of the strings is JSON and one isn't. We're going
   133  			// to be a little inefficient here, but we can just reuse another
   134  			// renderer for this so let's keep it simple.
   135  			return computed.NewDiff(
   136  				TypeChange(
   137  					computed.NewDiff(Primitive(renderer.before, nil, cty.String), plans.Delete, false),
   138  					computed.NewDiff(Primitive(nil, renderer.after, cty.String), plans.Create, false)),
   139  				diff.Action,
   140  				diff.Replace).RenderHuman(indent, opts)
   141  		}
   142  
   143  		if !beforeString.IsMultiline && !afterString.IsMultiline {
   144  			return fmt.Sprintf("%q %s %q%s", beforeString.String, opts.Colorize.Color("[yellow]->[reset]"), afterString.String, forcesReplacement(diff.Replace, opts))
   145  		}
   146  
   147  		beforeLines := strings.Split(beforeString.String, "\n")
   148  		afterLines := strings.Split(afterString.String, "\n")
   149  
   150  		processIndices := func(beforeIx, afterIx int) {
   151  			if beforeIx < 0 || beforeIx >= len(beforeLines) {
   152  				lines = append(lines, fmt.Sprintf("%s%s%s", formatIndent(indent+1), writeDiffActionSymbol(plans.Create, opts), afterLines[afterIx]))
   153  				return
   154  			}
   155  
   156  			if afterIx < 0 || afterIx >= len(afterLines) {
   157  				lines = append(lines, fmt.Sprintf("%s%s%s", formatIndent(indent+1), writeDiffActionSymbol(plans.Delete, opts), beforeLines[beforeIx]))
   158  				return
   159  			}
   160  
   161  			lines = append(lines, fmt.Sprintf("%s%s%s", formatIndent(indent+1), writeDiffActionSymbol(plans.NoOp, opts), beforeLines[beforeIx]))
   162  		}
   163  		isObjType := func(_ string) bool {
   164  			return false
   165  		}
   166  
   167  		collections.ProcessSlice(beforeLines, afterLines, processIndices, isObjType)
   168  	}
   169  
   170  	// We return early if we find non-multiline strings or JSON strings, so we
   171  	// know here that we just render the lines slice properly.
   172  	return fmt.Sprintf("<<-EOT%s\n%s\n%s%sEOT%s",
   173  		forcesReplacement(diff.Replace, opts),
   174  		strings.Join(lines, "\n"),
   175  		formatIndent(indent),
   176  		writeDiffActionSymbol(plans.NoOp, opts),
   177  		nullSuffix(diff.Action, opts))
   178  }
   179  
   180  func (renderer primitiveRenderer) renderStringDiffAsJson(diff computed.Diff, indent int, opts computed.RenderHumanOpts, before evaluatedString, after evaluatedString) string {
   181  	jsonDiff := RendererJsonOpts().Transform(before.Json, after.Json, diff.Action != plans.Create, diff.Action != plans.Delete, attribute_path.AlwaysMatcher())
   182  
   183  	action := diff.Action
   184  
   185  	jsonOpts := opts.Clone()
   186  	jsonOpts.OverrideNullSuffix = true
   187  
   188  	var whitespace, replace string
   189  	if jsonDiff.Action == plans.NoOp && diff.Action == plans.Update {
   190  		// Then this means we are rendering a whitespace only change. The JSON
   191  		// differ will have ignored the whitespace changes so that makes the
   192  		// diff we are about to print out very confusing without extra
   193  		// explanation.
   194  		if diff.Replace {
   195  			whitespace = " # whitespace changes force replacement"
   196  		} else {
   197  			whitespace = " # whitespace changes"
   198  		}
   199  
   200  		// Because we'd be showing no changes otherwise:
   201  		jsonOpts.ShowUnchangedChildren = true
   202  
   203  		// Whitespace changes should not appear as if edited.
   204  		action = plans.NoOp
   205  	} else {
   206  		// We only show the replace suffix if we didn't print something out
   207  		// about whitespace changes.
   208  		replace = forcesReplacement(diff.Replace, opts)
   209  	}
   210  
   211  	renderedJsonDiff := jsonDiff.RenderHuman(indent+1, jsonOpts)
   212  
   213  	if diff.Action == plans.Create || diff.Action == plans.Delete {
   214  		// We don't display the '+' or '-' symbols on the JSON diffs, we should
   215  		// still display the '~' for an update action though.
   216  		action = plans.NoOp
   217  	}
   218  
   219  	if strings.Contains(renderedJsonDiff, "\n") {
   220  		return fmt.Sprintf("jsonencode(%s\n%s%s%s%s\n%s%s)%s", whitespace, formatIndent(indent+1), writeDiffActionSymbol(action, opts), renderedJsonDiff, replace, formatIndent(indent), writeDiffActionSymbol(plans.NoOp, opts), nullSuffix(diff.Action, opts))
   221  	}
   222  	return fmt.Sprintf("jsonencode(%s)%s%s", renderedJsonDiff, whitespace, replace)
   223  }