diff --git a/command/arguments/view.go b/command/arguments/view.go new file mode 100644 index 000000000..3d6372b6a --- /dev/null +++ b/command/arguments/view.go @@ -0,0 +1,43 @@ +package arguments + +// View represents the global command-line arguments which configure the view. +type View struct { + // NoColor is used to disable the use of terminal color codes in all + // output. + NoColor bool + + // CompactWarnings is used to coalesce duplicate warnings, to reduce the + // level of noise when multiple instances of the same warning are raised + // for a configuration. + CompactWarnings bool +} + +// ParseView processes CLI arguments, returning a View value and a +// possibly-modified slice of arguments. If any of the supported flags are +// found, they will be removed from the slice. +func ParseView(args []string) (*View, []string) { + common := &View{} + + // Keep track of the length of the returned slice. When we find an + // argument we support, i will not be incremented. + i := 0 + for _, v := range args { + switch v { + case "-no-color": + common.NoColor = true + case "-compact-warnings": + common.CompactWarnings = true + default: + // Unsupported argument: move left to the current position, and + // increment the index. + args[i] = v + i++ + } + } + + // Reduce the slice to the number of unsupported arguments. Any remaining + // to the right of i have already been moved left. + args = args[:i] + + return common, args +} diff --git a/command/arguments/view_test.go b/command/arguments/view_test.go new file mode 100644 index 000000000..d2e7c3f73 --- /dev/null +++ b/command/arguments/view_test.go @@ -0,0 +1,62 @@ +package arguments + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestParseView(t *testing.T) { + testCases := map[string]struct { + args []string + want *View + wantArgs []string + }{ + "nil": { + nil, + &View{NoColor: false, CompactWarnings: false}, + nil, + }, + "empty": { + []string{}, + &View{NoColor: false, CompactWarnings: false}, + []string{}, + }, + "none matching": { + []string{"-foo", "bar", "-baz"}, + &View{NoColor: false, CompactWarnings: false}, + []string{"-foo", "bar", "-baz"}, + }, + "no-color": { + []string{"-foo", "-no-color", "-baz"}, + &View{NoColor: true, CompactWarnings: false}, + []string{"-foo", "-baz"}, + }, + "compact-warnings": { + []string{"-foo", "-compact-warnings", "-baz"}, + &View{NoColor: false, CompactWarnings: true}, + []string{"-foo", "-baz"}, + }, + "both": { + []string{"-foo", "-no-color", "-compact-warnings", "-baz"}, + &View{NoColor: true, CompactWarnings: true}, + []string{"-foo", "-baz"}, + }, + "both, resulting in empty args": { + []string{"-no-color", "-compact-warnings"}, + &View{NoColor: true, CompactWarnings: true}, + []string{}, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, gotArgs := ParseView(tc.args) + if *got != *tc.want { + t.Errorf("unexpected result\n got: %#v\nwant: %#v", got, tc.want) + } + if !cmp.Equal(gotArgs, tc.wantArgs) { + t.Errorf("unexpected args\n got: %#v\nwant: %#v", gotArgs, tc.wantArgs) + } + }) + } +} diff --git a/command/meta.go b/command/meta.go index 92ad6b85a..4f01af630 100644 --- a/command/meta.go +++ b/command/meta.go @@ -621,10 +621,6 @@ func (m *Meta) process(args []string) []string { }, } - if m.View != nil { - m.View.EnableColor(m.Color) - } - return args } diff --git a/command/output.go b/command/output.go index 4eaec982a..52e4b942a 100644 --- a/command/output.go +++ b/command/output.go @@ -17,7 +17,9 @@ type OutputCommand struct { } func (c *OutputCommand) Run(rawArgs []string) int { - rawArgs = c.Meta.process(rawArgs) + // Parse and apply global view arguments + common, rawArgs := arguments.ParseView(rawArgs) + c.View.Configure(common) // Parse and validate flags args, diags := arguments.ParseOutput(rawArgs) diff --git a/command/output_test.go b/command/output_test.go index 36efa114c..3824c0fdc 100644 --- a/command/output_test.go +++ b/command/output_test.go @@ -149,6 +149,7 @@ func TestOutput_emptyOutputs(t *testing.T) { } args := []string{ + "-no-color", "-state", statePath, } code := c.Run(args) diff --git a/command/views/view.go b/command/views/view.go index a5dd446b6..5dd75184f 100644 --- a/command/views/view.go +++ b/command/views/view.go @@ -3,6 +3,7 @@ package views import ( "fmt" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/format" "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/tfdiags" @@ -41,11 +42,10 @@ func NewView(streams *terminal.Streams) *View { } } -// EnableColor controls the colorize implementation used by this view (and any -// other views which depend on it). This is called by the code which processes -// the global -no-color CLI flag, which happens after the view is initialized. -func (v *View) EnableColor(color bool) { - v.colorize.Disable = !color +// Configure applies the global view configuration flags. +func (v *View) Configure(view *arguments.View) { + v.colorize.Disable = view.NoColor + v.compactWarnings = view.CompactWarnings } // SetConfigSources overrides the default no-op callback with a new function