Skip to content
Open
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
27 changes: 24 additions & 3 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,34 @@ type Command struct {
// For commands with parents this ensures that the parent commands
// are part of the command path.
func (cmd *Command) FullName() string {
namePath := []string{}
return strings.Join(cmd.Path(), " ")
}

// Path returns the path of command names from the root to cmd, inclusive.
// Each element is a Command.Name. FullName() is equivalent to
// strings.Join(cmd.Path(), " ").
func (cmd *Command) Path() []string {
if cmd.parent != nil {
namePath = append(namePath, cmd.parent.FullName())
return append(cmd.parent.Path(), cmd.Name)
}
return []string{cmd.Name}
}

return strings.Join(append(namePath, cmd.Name), " ")
// Walk visits cmd and every descendant in pre-order. Returning a non-nil
// error from fn short-circuits the walk and is returned to the caller.
func (cmd *Command) Walk(fn func(*Command) error) error {
if fn == nil {
return nil
}
if err := fn(cmd); err != nil {
return err
}
for _, sub := range cmd.Commands {
if err := sub.Walk(fn); err != nil {
return err
}
}
return nil
}

func (cmd *Command) Command(name string) *Command {
Expand Down
83 changes: 83 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5827,3 +5827,86 @@ func TestCommand_NoDefaultCmdArgMatchingFlag(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &expectedArgs, actualArgs)
}

func TestCommand_Path(t *testing.T) {
subCmd := &Command{Name: "bar"}
subSubCmd := &Command{Name: "baz"}
subCmd.Commands = []*Command{subSubCmd}

cmd := &Command{
Name: "foo",
Commands: []*Command{subCmd},
}

require.NoError(t, cmd.Run(buildTestContext(t), []string{"foo", "bar", "baz"}))

assert.Equal(t, []string{"foo"}, cmd.Path())
assert.Equal(t, []string{"foo", "bar"}, subCmd.Path())
assert.Equal(t, []string{"foo", "bar", "baz"}, subSubCmd.Path())
}

func TestCommand_Walk(t *testing.T) {
subCmd := &Command{Name: "bar"}
subSubCmd := &Command{Name: "baz"}
subCmd.Commands = []*Command{subSubCmd}

cmd := &Command{
Name: "foo",
Commands: []*Command{subCmd},
}

var visited []string
err := cmd.Walk(func(c *Command) error {
visited = append(visited, c.Name)
return nil
})
require.NoError(t, err)
assert.Equal(t, []string{"foo", "bar", "baz"}, visited)
}

func TestCommand_Walk_ShortCircuit(t *testing.T) {
subCmd := &Command{Name: "bar"}
subSubCmd := &Command{Name: "baz"}
subCmd.Commands = []*Command{subSubCmd}

cmd := &Command{
Name: "foo",
Commands: []*Command{subCmd},
}

errWalk := fmt.Errorf("stop")
var visited []string
err := cmd.Walk(func(c *Command) error {
visited = append(visited, c.Name)
if c.Name == "bar" {
return errWalk
}
return nil
})
assert.ErrorIs(t, err, errWalk)
assert.Equal(t, []string{"foo", "bar"}, visited)
}

func TestCommand_Walk_Hidden(t *testing.T) {
subCmd := &Command{Name: "bar", HideHelp: true}
subSubCmd := &Command{Name: "baz"}
subCmd.Commands = []*Command{subSubCmd}

cmd := &Command{
Name: "foo",
Commands: []*Command{subCmd},
}

var visited []string
err := cmd.Walk(func(c *Command) error {
visited = append(visited, c.Name)
return nil
})
require.NoError(t, err)
assert.Equal(t, []string{"foo", "bar", "baz"}, visited)
}

func TestCommand_Walk_NilFn(t *testing.T) {
cmd := &Command{Name: "foo"}
assert.Nil(t, cmd.Walk(nil))
}
9 changes: 9 additions & 0 deletions godoc-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,11 @@ func (cmd *Command) Names() []string
func (cmd *Command) NumFlags() int
NumFlags returns the number of flags set

func (cmd *Command) Path() []string
Path returns the path of command names from the root to cmd,
inclusive. Each element is a Command.Name. FullName() is equivalent to
strings.Join(cmd.Path(), " ").

func (cmd *Command) Root() *Command
Root returns the Command at the root of the graph

Expand Down Expand Up @@ -801,6 +806,10 @@ func (cmd *Command) VisiblePersistentFlags() []Flag
VisiblePersistentFlags returns a slice of LocalFlag with Persistent=true and
Hidden=false.

func (cmd *Command) Walk(fn func(*Command) error) error
Walk visits cmd and every descendant in pre-order. Returning a non-nil error
from fn short-circuits the walk and is returned to the caller.

type CommandCategories interface {
// AddCommand adds a command to a category, creating a new category if necessary.
AddCommand(category string, command *Command)
Expand Down
9 changes: 9 additions & 0 deletions testdata/godoc-v3.x.txt
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,11 @@ func (cmd *Command) Names() []string
func (cmd *Command) NumFlags() int
NumFlags returns the number of flags set

func (cmd *Command) Path() []string
Path returns the path of command names from the root to cmd,
inclusive. Each element is a Command.Name. FullName() is equivalent to
strings.Join(cmd.Path(), " ").

func (cmd *Command) Root() *Command
Root returns the Command at the root of the graph

Expand Down Expand Up @@ -801,6 +806,10 @@ func (cmd *Command) VisiblePersistentFlags() []Flag
VisiblePersistentFlags returns a slice of LocalFlag with Persistent=true and
Hidden=false.

func (cmd *Command) Walk(fn func(*Command) error) error
Walk visits cmd and every descendant in pre-order. Returning a non-nil error
from fn short-circuits the walk and is returned to the caller.

type CommandCategories interface {
// AddCommand adds a command to a category, creating a new category if necessary.
AddCommand(category string, command *Command)
Expand Down
Loading