github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/views/view.go (about) 1 package views 2 3 import ( 4 "github.com/hashicorp/terraform/internal/command/arguments" 5 "github.com/hashicorp/terraform/internal/command/format" 6 "github.com/hashicorp/terraform/internal/terminal" 7 "github.com/hashicorp/terraform/internal/tfdiags" 8 "github.com/mitchellh/colorstring" 9 ) 10 11 // View is the base layer for command views, encapsulating a set of I/O 12 // streams, a colorize implementation, and implementing a human friendly view 13 // for diagnostics. 14 type View struct { 15 streams *terminal.Streams 16 colorize *colorstring.Colorize 17 18 compactWarnings bool 19 20 // When this is true it's a hint that Terraform is being run indirectly 21 // via a wrapper script or other automation and so we may wish to replace 22 // direct examples of commands to run with more conceptual directions. 23 // However, we only do this on a best-effort basis, typically prioritizing 24 // the messages that users are most likely to see. 25 runningInAutomation bool 26 27 // This unfortunate wart is required to enable rendering of diagnostics which 28 // have associated source code in the configuration. This function pointer 29 // will be dereferenced as late as possible when rendering diagnostics in 30 // order to access the config loader cache. 31 configSources func() map[string][]byte 32 } 33 34 // Initialize a View with the given streams, a disabled colorize object, and a 35 // no-op configSources callback. 36 func NewView(streams *terminal.Streams) *View { 37 return &View{ 38 streams: streams, 39 colorize: &colorstring.Colorize{ 40 Colors: colorstring.DefaultColors, 41 Disable: true, 42 Reset: true, 43 }, 44 configSources: func() map[string][]byte { return nil }, 45 } 46 } 47 48 // SetRunningInAutomation modifies the view's "running in automation" flag, 49 // which causes some slight adjustments to certain messages that would normally 50 // suggest specific Terraform commands to run, to make more conceptual gestures 51 // instead for situations where the user isn't running Terraform directly. 52 // 53 // For convenient use during initialization (in conjunction with NewView), 54 // SetRunningInAutomation returns the reciever after modifying it. 55 func (v *View) SetRunningInAutomation(new bool) *View { 56 v.runningInAutomation = new 57 return v 58 } 59 60 func (v *View) RunningInAutomation() bool { 61 return v.runningInAutomation 62 } 63 64 // Configure applies the global view configuration flags. 65 func (v *View) Configure(view *arguments.View) { 66 v.colorize.Disable = view.NoColor 67 v.compactWarnings = view.CompactWarnings 68 } 69 70 // SetConfigSources overrides the default no-op callback with a new function 71 // pointer, and should be called when the config loader is initialized. 72 func (v *View) SetConfigSources(cb func() map[string][]byte) { 73 v.configSources = cb 74 } 75 76 // Diagnostics renders a set of warnings and errors in human-readable form. 77 // Warnings are printed to stdout, and errors to stderr. 78 func (v *View) Diagnostics(diags tfdiags.Diagnostics) { 79 diags.Sort() 80 81 if len(diags) == 0 { 82 return 83 } 84 85 diags = diags.ConsolidateWarnings(1) 86 87 // Since warning messages are generally competing 88 if v.compactWarnings { 89 // If the user selected compact warnings and all of the diagnostics are 90 // warnings then we'll use a more compact representation of the warnings 91 // that only includes their summaries. 92 // We show full warnings if there are also errors, because a warning 93 // can sometimes serve as good context for a subsequent error. 94 useCompact := true 95 for _, diag := range diags { 96 if diag.Severity() != tfdiags.Warning { 97 useCompact = false 98 break 99 } 100 } 101 if useCompact { 102 msg := format.DiagnosticWarningsCompact(diags, v.colorize) 103 msg = "\n" + msg + "\nTo see the full warning notes, run Terraform without -compact-warnings.\n" 104 v.streams.Print(msg) 105 return 106 } 107 } 108 109 for _, diag := range diags { 110 var msg string 111 if v.colorize.Disable { 112 msg = format.DiagnosticPlain(diag, v.configSources(), v.streams.Stderr.Columns()) 113 } else { 114 msg = format.Diagnostic(diag, v.configSources(), v.colorize, v.streams.Stderr.Columns()) 115 } 116 117 if diag.Severity() == tfdiags.Error { 118 v.streams.Eprint(msg) 119 } else { 120 v.streams.Print(msg) 121 } 122 } 123 } 124 125 // HelpPrompt is intended to be called from commands which fail to parse all 126 // of their CLI arguments successfully. It refers users to the full help output 127 // rather than rendering it directly, which can be overwhelming and confusing. 128 func (v *View) HelpPrompt(command string) { 129 v.streams.Eprintf(helpPrompt, command) 130 } 131 132 const helpPrompt = ` 133 For more help on using this command, run: 134 terraform %s -help 135 ` 136 137 // outputColumns returns the number of text character cells any non-error 138 // output should be wrapped to. 139 // 140 // This is the number of columns to use if you are calling v.streams.Print or 141 // related functions. 142 func (v *View) outputColumns() int { 143 return v.streams.Stdout.Columns() 144 } 145 146 // errorColumns returns the number of text character cells any error 147 // output should be wrapped to. 148 // 149 // This is the number of columns to use if you are calling v.streams.Eprint 150 // or related functions. 151 func (v *View) errorColumns() int { 152 return v.streams.Stderr.Columns() 153 } 154 155 // outputHorizRule will call v.streams.Println with enough horizontal line 156 // characters to fill an entire row of output. 157 // 158 // If UI color is enabled, the rule will get a dark grey coloring to try to 159 // visually de-emphasize it. 160 func (v *View) outputHorizRule() { 161 v.streams.Println(format.HorizontalRule(v.colorize, v.outputColumns())) 162 }