Repository: opentofu/opentofu
File: internal/command/jsonformat/computed/renderers/primitive.go (Go)

Code before:
	if renderer.ctype == cty.String {
		return renderer.renderStringDiff(diff, indent, opts)
	}

	beforeValue := renderPrimitiveValue(renderer.before, renderer.ctype, opts)
	afterValue := renderPrimitiveValue(renderer.after, renderer.ctype, opts)

	switch diff.Action {
	case plans.Create:
		return fmt.Sprintf("%s%s", afterValue, forcesReplacement(diff.Replace, opts))
	case plans.Delete:
		return fmt.Sprintf("%s%s%s", beforeValue, nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts))
	case plans.NoOp:
		return fmt.Sprintf("%s%s", beforeValue, forcesReplacement(diff.Replace, opts))
	default:
		return fmt.Sprintf("%s %s %s%s", beforeValue, opts.Colorize.Color("[yellow]->[reset]"), afterValue, forcesReplacement(diff.Replace, opts))
	}
}

func renderPrimitiveValue(value interface{}, t cty.Type, opts computed.RenderHumanOpts) string {
	if value == nil {
		return opts.Colorize.Color("[dark_gray]null[reset]")
	}

	switch {
	case t == cty.Bool:
		if value.(bool) {
			return "true"
		}
		return "false"
	case t == cty.Number:
		num := value.(json.Number)
		return num.String()
	default:
		panic("unrecognized primitive type: " + t.FriendlyName())
	}
}

func (renderer primitiveRenderer) renderStringDiff(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {

	// We process multiline strings at the end of the switch statement.
	var lines []string

	switch diff.Action {
	case plans.Create, plans.NoOp:
		str := evaluatePrimitiveString(renderer.after, opts)

		if str.Json != nil {
			if diff.Action == plans.NoOp {
				return renderer.renderStringDiffAsJson(diff, indent, opts, str, str)
			} else {

Review comment (bug):
I think cases like this are going to be interesting as we continue, because `switch` over `cty.Type` has historically typically been written in this style because not all of the cty types are comparable with `==`, and so we sometimes end up with interesting combinations like this:

```go
switch {
    case t == cty.String:
        // ...
    case t.IsListType() && t.ElementType() == cty.String:
        // ...
    /...
}
```

In cases like this where all of the arms are just using `==` comparisons anyway there's not any strong reason to write it the other way, but this approach does mean there will be less consistency in the way we handle type-matching in different parts of the codebase (depending on exactly what types are being matched) vs. the existing mostly-standard of just _always_ using individual boolean expressions on each `case`.

Not a big deal but something I think we should keep an eye on as we continue working, in particular to make sure we don't inadvertently introduce incorrect uses of `==` for `cty.Type` values that are not comparable in that way.


Code after:
	if renderer.ctype == cty.String {
		return renderer.renderStringDiff(diff, indent, opts)
	}

	beforeValue := renderPrimitiveValue(renderer.before, renderer.ctype, opts)
	afterValue := renderPrimitiveValue(renderer.after, renderer.ctype, opts)

	switch diff.Action {
	case plans.Create:
		return fmt.Sprintf("%s%s", afterValue, forcesReplacement(diff.Replace, opts))
	case plans.Delete:
		return fmt.Sprintf("%s%s%s", beforeValue, nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts))
	case plans.NoOp:
		return fmt.Sprintf("%s%s", beforeValue, forcesReplacement(diff.Replace, opts))
	default:
		return fmt.Sprintf("%s %s %s%s", beforeValue, opts.Colorize.Color("[yellow]->[reset]"), afterValue, forcesReplacement(diff.Replace, opts))
	}
}

func renderPrimitiveValue(value interface{}, t cty.Type, opts computed.RenderHumanOpts) string {
	if value == nil {
		return opts.Colorize.Color("[dark_gray]null[reset]")
	}

	switch t {
	case cty.Bool:
		if value.(bool) {
			return "true"
		}
		return "false"
	case cty.Number:
		num := value.(json.Number)
		return num.String()
	default:
		panic("unrecognized primitive type: " + t.FriendlyName())
	}
}

func (renderer primitiveRenderer) renderStringDiff(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {

	// We process multiline strings at the end of the switch statement.
	var lines []string

	switch diff.Action {
	case plans.Create, plans.NoOp:
		str := evaluatePrimitiveString(renderer.after, opts)

		if str.Json != nil {
			if diff.Action == plans.NoOp {
				return renderer.renderStringDiffAsJson(diff, indent, opts, str, str)
			} else {