Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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