| // Copyright (c) The OpenTofu Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| // Copyright (c) 2023 HashiCorp, Inc. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| package renderers | |
| import ( | |
| "fmt" | |
| "strings" | |
| "github.com/hashicorp/hcl/v2/hclsyntax" | |
| "github.com/opentofu/opentofu/internal/command/jsonformat/computed" | |
| "github.com/opentofu/opentofu/internal/plans" | |
| ) | |
| // NoWarningsRenderer defines a Warnings function that returns an empty list of | |
| // warnings. This can be used by other renderers to ensure we don't see lots of | |
| // repeats of this empty function. | |
| type NoWarningsRenderer struct{} | |
| // WarningsHuman returns an empty slice, as the name NoWarningsRenderer suggests. | |
| func (render NoWarningsRenderer) WarningsHuman(_ computed.Diff, _ int, _ computed.RenderHumanOpts) []string { | |
| return nil | |
| } | |
| // nullSuffix returns the `-> null` suffix if the change is a delete action, and | |
| // it has not been overridden. | |
| func nullSuffix(action plans.Action, opts computed.RenderHumanOpts) string { | |
| if !opts.OverrideNullSuffix && action == plans.Delete { | |
| return opts.Colorize.Color(" [dark_gray]-> null[reset]") | |
| } | |
| return "" | |
| } | |
| // forcesReplacement returns the `# forces replacement` suffix if this change is | |
| // driving the entire resource to be replaced. | |
| func forcesReplacement(replace bool, opts computed.RenderHumanOpts) string { | |
| if replace || opts.OverrideForcesReplacement { | |
| return opts.Colorize.Color(" [red]# forces replacement[reset]") | |
| } | |
| return "" | |
| } | |
| // indent returns whitespace that is the required length for the specified | |
| // indent. | |
| func formatIndent(indent int) string { | |
| return strings.Repeat(" ", indent) | |
| } | |
| // unchanged prints out a description saying how many of 'keyword' have been | |
| // hidden because they are unchanged or noop actions. | |
| func unchanged(keyword string, count int, opts computed.RenderHumanOpts) string { | |
| if count == 1 { | |
| return opts.Colorize.Color(fmt.Sprintf("[dark_gray]# (%d unchanged %s hidden)[reset]", count, keyword)) | |
| } | |
| return opts.Colorize.Color(fmt.Sprintf("[dark_gray]# (%d unchanged %ss hidden)[reset]", count, keyword)) | |
| } | |
| // EnsureValidAttributeName checks if `name` contains any HCL syntax and calls | |
| // and returns hclEscapeString. | |
| func EnsureValidAttributeName(name string) string { | |
| if !hclsyntax.ValidIdentifier(name) { | |
| return hclEscapeString(name) | |
| } | |
| return name | |
| } | |
| // hclEscapeString formats the input string into a format that is safe for | |
| // rendering within HCL. | |
| // | |
| // Note, this function doesn't actually do a very good job of this currently. We | |
| // need to expose some internal functions from HCL in a future version and call | |
| // them from here. For now, just use "%q" formatting. | |
| func hclEscapeString(str string) string { | |
| // TODO: Replace this with more complete HCL logic instead of the simple | |
| // go workaround. | |
| return fmt.Sprintf("%q", str) | |
| } | |
| // DiffActionSymbol returns a string that, once passed through a | |
| // colorstring.Colorize, will produce a result that can be written | |
| // to a terminal to produce a symbol made of three printable | |
| // characters, possibly interspersed with VT100 color codes. | |
| func DiffActionSymbol(action plans.Action) string { | |
| switch action { | |
| case plans.DeleteThenCreate: | |
| return "[red]-[reset]/[green]+[reset]" | |
| case plans.CreateThenDelete: | |
| return "[green]+[reset]/[red]-[reset]" | |
| case plans.ForgetThenCreate: | |
| return " [red].[reset]/[green]+[reset]" | |
| case plans.Create: | |
| return " [green]+[reset]" | |
| case plans.Delete: | |
| return " [red]-[reset]" | |
| case plans.Read: | |
| return " [cyan]<=[reset]" | |
| case plans.Update: | |
| return " [yellow]~[reset]" | |
| case plans.NoOp: | |
| return " " | |
| case plans.Forget: | |
| return " [red].[reset]" | |
| default: | |
| return " ?" | |
| } | |
| } | |
| // writeDiffActionSymbol writes out the symbols for the associated action, and | |
| // handles localized colorization of the symbol as well as indenting the symbol | |
| // to be 4 spaces wide. | |
| // | |
| // If the opts has HideDiffActionSymbols set then this function returns an empty | |
| // string. | |
| func writeDiffActionSymbol(action plans.Action, opts computed.RenderHumanOpts) string { | |
| if opts.HideDiffActionSymbols { | |
| return "" | |
| } | |
| return fmt.Sprintf("%s ", opts.Colorize.Color(DiffActionSymbol(action))) | |
| } |