Add CalledAs
method to cobra.Command (w/ tests) (#567)
* Add `CalledAs` method to Command (w/ tests) The `CalledAs` method returns the name of the command or alias that invoked the command -- as long as the command was actually invoked. Otherwise, it returns the empty string. The opens up possibilies for commands to behave differently based on which alias invoked the command (in the same vein as Linux programs which adjust their behavior based on the value of argv[0]). * Fixed formatting
This commit is contained in:
parent
9979838ec4
commit
eb58983359
23
command.go
23
command.go
@ -147,6 +147,11 @@ type Command struct {
|
|||||||
commandsMaxNameLen int
|
commandsMaxNameLen int
|
||||||
// commandsAreSorted defines, if command slice are sorted or not.
|
// commandsAreSorted defines, if command slice are sorted or not.
|
||||||
commandsAreSorted bool
|
commandsAreSorted bool
|
||||||
|
// commandCalledAs is the name or alias value used to call this command.
|
||||||
|
commandCalledAs struct {
|
||||||
|
name string
|
||||||
|
called bool
|
||||||
|
}
|
||||||
|
|
||||||
// args is actual args parsed from flags.
|
// args is actual args parsed from flags.
|
||||||
args []string
|
args []string
|
||||||
@ -557,6 +562,7 @@ func (c *Command) findNext(next string) *Command {
|
|||||||
matches := make([]*Command, 0)
|
matches := make([]*Command, 0)
|
||||||
for _, cmd := range c.commands {
|
for _, cmd := range c.commands {
|
||||||
if cmd.Name() == next || cmd.HasAlias(next) {
|
if cmd.Name() == next || cmd.HasAlias(next) {
|
||||||
|
cmd.commandCalledAs.name = next
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
if EnablePrefixMatching && cmd.hasNameOrAliasPrefix(next) {
|
if EnablePrefixMatching && cmd.hasNameOrAliasPrefix(next) {
|
||||||
@ -567,6 +573,7 @@ func (c *Command) findNext(next string) *Command {
|
|||||||
if len(matches) == 1 {
|
if len(matches) == 1 {
|
||||||
return matches[0]
|
return matches[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,6 +835,11 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
|||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.commandCalledAs.called = true
|
||||||
|
if cmd.commandCalledAs.name == "" {
|
||||||
|
cmd.commandCalledAs.name = cmd.Name()
|
||||||
|
}
|
||||||
|
|
||||||
err = cmd.execute(flags)
|
err = cmd.execute(flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Always show help if requested, even if SilenceErrors is in
|
// Always show help if requested, even if SilenceErrors is in
|
||||||
@ -1135,14 +1147,25 @@ func (c *Command) HasAlias(s string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CalledAs returns the command name or alias that was used to invoke
|
||||||
|
// this command or an empty string if the command has not been called.
|
||||||
|
func (c *Command) CalledAs() string {
|
||||||
|
if c.commandCalledAs.called {
|
||||||
|
return c.commandCalledAs.name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// hasNameOrAliasPrefix returns true if the Name or any of aliases start
|
// hasNameOrAliasPrefix returns true if the Name or any of aliases start
|
||||||
// with prefix
|
// with prefix
|
||||||
func (c *Command) hasNameOrAliasPrefix(prefix string) bool {
|
func (c *Command) hasNameOrAliasPrefix(prefix string) bool {
|
||||||
if strings.HasPrefix(c.Name(), prefix) {
|
if strings.HasPrefix(c.Name(), prefix) {
|
||||||
|
c.commandCalledAs.name = c.Name()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for _, alias := range c.Aliases {
|
for _, alias := range c.Aliases {
|
||||||
if strings.HasPrefix(alias, prefix) {
|
if strings.HasPrefix(alias, prefix) {
|
||||||
|
c.commandCalledAs.name = alias
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1563,3 +1563,66 @@ func TestUpdateName(t *testing.T) {
|
|||||||
t.Error("c.Name() should be updated on changed c.Use")
|
t.Error("c.Name() should be updated on changed c.Use")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type calledAsTestcase struct {
|
||||||
|
args []string
|
||||||
|
call string
|
||||||
|
want string
|
||||||
|
epm bool
|
||||||
|
tc bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *calledAsTestcase) test(t *testing.T) {
|
||||||
|
defer func(ov bool) { EnablePrefixMatching = ov }(EnablePrefixMatching)
|
||||||
|
EnablePrefixMatching = tc.epm
|
||||||
|
|
||||||
|
var called *Command
|
||||||
|
run := func(c *Command, _ []string) { t.Logf("called: %q", c.Name()); called = c }
|
||||||
|
|
||||||
|
parent := &Command{Use: "parent", Run: run}
|
||||||
|
child1 := &Command{Use: "child1", Run: run, Aliases: []string{"this"}}
|
||||||
|
child2 := &Command{Use: "child2", Run: run, Aliases: []string{"that"}}
|
||||||
|
|
||||||
|
parent.AddCommand(child1)
|
||||||
|
parent.AddCommand(child2)
|
||||||
|
parent.SetArgs(tc.args)
|
||||||
|
|
||||||
|
output := new(bytes.Buffer)
|
||||||
|
parent.SetOutput(output)
|
||||||
|
|
||||||
|
parent.Execute()
|
||||||
|
|
||||||
|
if called == nil {
|
||||||
|
if tc.call != "" {
|
||||||
|
t.Errorf("missing expected call to command: %s", tc.call)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if called.Name() != tc.call {
|
||||||
|
t.Errorf("called command == %q; Wanted %q", called.Name(), tc.call)
|
||||||
|
} else if got := called.CalledAs(); got != tc.want {
|
||||||
|
t.Errorf("%s.CalledAs() == %q; Wanted: %q", tc.call, got, tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalledAs(t *testing.T) {
|
||||||
|
tests := map[string]calledAsTestcase{
|
||||||
|
"find/no-args": {nil, "parent", "parent", false, false},
|
||||||
|
"find/real-name": {[]string{"child1"}, "child1", "child1", false, false},
|
||||||
|
"find/full-alias": {[]string{"that"}, "child2", "that", false, false},
|
||||||
|
"find/part-no-prefix": {[]string{"thi"}, "", "", false, false},
|
||||||
|
"find/part-alias": {[]string{"thi"}, "child1", "this", true, false},
|
||||||
|
"find/conflict": {[]string{"th"}, "", "", true, false},
|
||||||
|
"traverse/no-args": {nil, "parent", "parent", false, true},
|
||||||
|
"traverse/real-name": {[]string{"child1"}, "child1", "child1", false, true},
|
||||||
|
"traverse/full-alias": {[]string{"that"}, "child2", "that", false, true},
|
||||||
|
"traverse/part-no-prefix": {[]string{"thi"}, "", "", false, true},
|
||||||
|
"traverse/part-alias": {[]string{"thi"}, "child1", "this", true, true},
|
||||||
|
"traverse/conflict": {[]string{"th"}, "", "", true, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
t.Run(name, tc.test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user