diff --git a/args.go b/args.go index ed1e70c..08f92f1 100644 --- a/args.go +++ b/args.go @@ -129,3 +129,16 @@ func MatchAll(pargs ...PositionalArgs) PositionalArgs { func ExactValidArgs(n int) PositionalArgs { return MatchAll(ExactArgs(n), OnlyValidArgs) } + +// WithUsage wraps another PositionalArgs function. If the function fails +// (returns a non-nil error), the error is supplemented with the command's +// usage message, providing the user with the information required to run the +// command correctly. +func WithUsage(wrapped PositionalArgs) PositionalArgs { + return func(cmd *Command, args []string) error { + if err := wrapped(cmd, args); err != nil { + return fmt.Errorf("%w\n\n%s", err, cmd.UsageString()) + } + return nil + } +} diff --git a/args_test.go b/args_test.go index 90d174c..ec17fdc 100644 --- a/args_test.go +++ b/args_test.go @@ -539,3 +539,42 @@ func TestLegacyArgsSubcmdAcceptsArgs(t *testing.T) { t.Fatalf("Unexpected error: %v", err) } } + +// WithUsage + +func TestWithUsage(t *testing.T) { + c := getCommand(WithUsage(ExactArgs(1)), false) + + _, err := executeCommand(c /* no args */) + if err == nil { + t.Fatalf("Expected error, got nil") + } + + got, want := err.Error(), c.UsageString() + if !strings.Contains(got, want) { + t.Errorf("Expected error containing %q, got %q", want, got) + } +} + +func ExampleWithUsage() { + cmd := &Command{ + Use: "example ", + Args: WithUsage(ExactArgs(1)), + Run: func(*Command, []string) { + panic("not reached") + }, + } + + cmd.SetArgs([]string{"1", "2"}) + err := cmd.Execute() + fmt.Print(err) + + // Output: + // accepts 1 arg(s), received 2 + // + // Usage: + // example [flags] + // + // Flags: + // -h, --help help for example +}