From 0c384e8cd8f5dc77a3b1341b0c600d63ecf779be Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Fri, 13 Feb 2026 14:40:44 +0100 Subject: [PATCH] refactor providers schema command argument parsing --- .../command/arguments/providers_schema.go | 50 +++++++++ .../arguments/providers_schema_test.go | 100 ++++++++++++++++++ internal/command/providers_schema.go | 21 +--- 3 files changed, 153 insertions(+), 18 deletions(-) create mode 100644 internal/command/arguments/providers_schema.go create mode 100644 internal/command/arguments/providers_schema_test.go diff --git a/internal/command/arguments/providers_schema.go b/internal/command/arguments/providers_schema.go new file mode 100644 index 0000000000..133ff3876c --- /dev/null +++ b/internal/command/arguments/providers_schema.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package arguments + +import "github.com/hashicorp/terraform/internal/tfdiags" + +// ProvidersSchema represents the command-line arguments for the providers +// schema command. +type ProvidersSchema struct { + JSON bool +} + +// ParseProvidersSchema processes CLI arguments, returning a ProvidersSchema +// value and errors. If errors are encountered, a ProvidersSchema value is +// still returned representing the best effort interpretation of the arguments. +func ParseProvidersSchema(args []string) (*ProvidersSchema, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + providersSchema := &ProvidersSchema{} + + cmdFlags := defaultFlagSet("providers schema") + cmdFlags.BoolVar(&providersSchema.JSON, "json", false, "produce JSON output") + + if err := cmdFlags.Parse(args); err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + err.Error(), + )) + } + + args = cmdFlags.Args() + if len(args) > 0 { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Too many command line arguments", + "Expected no positional arguments.", + )) + } + + if !providersSchema.JSON { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "The -json flag is required", + "The `terraform providers schema` command requires the `-json` flag.", + )) + } + + return providersSchema, diags +} diff --git a/internal/command/arguments/providers_schema_test.go b/internal/command/arguments/providers_schema_test.go new file mode 100644 index 0000000000..0ee55c974f --- /dev/null +++ b/internal/command/arguments/providers_schema_test.go @@ -0,0 +1,100 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package arguments + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/internal/tfdiags" +) + +func TestParseProvidersSchema_valid(t *testing.T) { + testCases := map[string]struct { + args []string + want *ProvidersSchema + }{ + "json": { + []string{"-json"}, + &ProvidersSchema{ + JSON: true, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseProvidersSchema(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Fatalf("unexpected result\n%s", diff) + } + }) + } +} + +func TestParseProvidersSchema_invalid(t *testing.T) { + testCases := map[string]struct { + args []string + want *ProvidersSchema + wantDiags tfdiags.Diagnostics + }{ + "missing json": { + nil, + &ProvidersSchema{ + JSON: false, + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "The -json flag is required", + "The `terraform providers schema` command requires the `-json` flag.", + ), + }, + }, + "too many positional arguments": { + []string{"-json", "extra"}, + &ProvidersSchema{ + JSON: true, + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "Too many command line arguments", + "Expected no positional arguments.", + ), + }, + }, + "unknown flag and missing json": { + []string{"-wat"}, + &ProvidersSchema{ + JSON: false, + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + "flag provided but not defined: -wat", + ), + tfdiags.Sourceless( + tfdiags.Error, + "The -json flag is required", + "The `terraform providers schema` command requires the `-json` flag.", + ), + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, gotDiags := ParseProvidersSchema(tc.args) + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Fatalf("unexpected result\n%s", diff) + } + tfdiags.AssertDiagnosticsMatch(t, gotDiags, tc.wantDiags) + }) + } +} diff --git a/internal/command/providers_schema.go b/internal/command/providers_schema.go index 064dfb760d..6af9b75f3a 100644 --- a/internal/command/providers_schema.go +++ b/internal/command/providers_schema.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/terraform/internal/backend/backendrun" "github.com/hashicorp/terraform/internal/command/arguments" "github.com/hashicorp/terraform/internal/command/jsonprovider" - "github.com/hashicorp/terraform/internal/tfdiags" ) // ProvidersCommand is a Command implementation that prints out information @@ -28,23 +27,12 @@ func (c *ProvidersSchemaCommand) Synopsis() string { } func (c *ProvidersSchemaCommand) Run(args []string) int { - args = c.Meta.process(args) - cmdFlags := c.Meta.defaultFlagSet("providers schema") - var jsonOutput bool - cmdFlags.BoolVar(&jsonOutput, "json", false, "produce JSON output") - - cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } - if err := cmdFlags.Parse(args); err != nil { - c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) + _, diags := arguments.ParseProvidersSchema(c.Meta.process(args)) + if diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - if !jsonOutput { - c.Ui.Error( - "The `terraform providers schema` command requires the `-json` flag.\n") - cmdFlags.Usage() - return 1 - } viewType := arguments.ViewJSON // See above; enforced use of JSON output // Check for user-supplied plugin path @@ -53,9 +41,6 @@ func (c *ProvidersSchemaCommand) Run(args []string) int { c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err)) return 1 } - - var diags tfdiags.Diagnostics - // Load the backend b, backendDiags := c.backend(".", viewType) diags = diags.Append(backendDiags)