Skip to content

Commit

Permalink
Fix bug where subcommand's positional args aren't captured (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
FollowTheProcess authored Oct 3, 2024
1 parent cdb7573 commit 869bad3
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 4 deletions.
10 changes: 6 additions & 4 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ func (c *Command) Execute() error {
// cmd here will be c.
cmd, args := findRequestedCommand(c, c.args)

// Below this point, use cmd not c!

if err := cmd.flagSet().Parse(args); err != nil {
return fmt.Errorf("failed to parse command flags: %w", err)
}
Expand Down Expand Up @@ -257,22 +259,22 @@ func (c *Command) Execute() error {
//
// We're modifying the slice in place here, hence not using a range loop as it
// would take a copy of the c.positionalArgs slice
for i := 0; i < len(c.positionalArgs); i++ {
for i := 0; i < len(cmd.positionalArgs); i++ {
if i >= len(argsWithoutFlags) {
arg := c.positionalArgs[i]
arg := cmd.positionalArgs[i]

// If we've fallen off the end of argsWithoutFlags and the positionalArg at this
// index does not have a default, it means the arg was required but not provided
if arg.defaultValue == "" {
return fmt.Errorf("missing required argument %q, expected at position %d", arg.name, i)
}
// It does have a default, so use that instead
c.positionalArgs[i].value = arg.defaultValue
cmd.positionalArgs[i].value = arg.defaultValue
} else {
// We are in a valid index in both slices which means the named positional
// argument at this index was provided on the command line, so all we need
// to do is set its value
c.positionalArgs[i].value = argsWithoutFlags[i]
cmd.positionalArgs[i].value = argsWithoutFlags[i]
}
}

Expand Down
21 changes: 21 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,18 @@ func TestSubCommandExecute(t *testing.T) {
}

func TestPositionalArgs(t *testing.T) {
sub := func() (*cli.Command, error) {
return cli.New(
"sub",
cli.Short("Sub command"),
cli.Arg("subarg", "Argument given to a subcommand", ""),
cli.Run(func(cmd *cli.Command, args []string) error {
fmt.Fprintf(cmd.Stdout(), "Hello from sub command, subarg: %s", cmd.Arg("subarg"))
return nil
}),
)
}

tests := []struct {
name string // The name of the test case
stdout string // The expected stdout
Expand Down Expand Up @@ -320,6 +332,15 @@ func TestPositionalArgs(t *testing.T) {
wantErr: true,
errMsg: `missing required argument "something", expected at position 2`,
},
{
name: "subcommand with named arg",
options: []cli.Option{
cli.SubCommands(sub),
},
stdout: "Hello from sub command, subarg: blah",
args: []string{"sub", "blah"}, // subarg should be "blah"
wantErr: false,
},
}

for _, tt := range tests {
Expand Down

0 comments on commit 869bad3

Please sign in to comment.